1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 1998-2018. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 /*
21  * Erlang port program to do the name service lookup for the erlang
22  * distribution and inet part of the kernel.
23  * A pool of subprocess is kept, to which a pair of pipes is connected.
24  * The main process schedules requests among the different subprocesses
25  * (created with fork()), to be able to handle as many requests as possible
26  * simultaneously. The controlling erlang machine may request a "cancel",
27  * in which case the process may be killed and restarted when the need arises.
28  * The single numeric parameter to this program is the maximum port pool size,
29  * which is the size of the bookkeeping array.
30  *
31  * Windows:
32  * There is instead of a pool of processes a pool of threads.
33  * Communication is not done through pipes but via message queues between
34  * the threads. The only "pipes" involved are the ones used for communicating
35  * with Erlang.
36  * Important note:
37  * For unknown reasons, the combination of a thread doing blocking I/O on
38  * a named pipe at the same time as another thread tries to resolve a hostname
39  * may (with certain software configurations) block the gethostbyname call (!)
40  * For that reason, standard input (and standard output) should be opened
41  * in asynchronous mode (FILE_FLAG_OVERLAPPED), which has to be done by Erlang.
42  * A special flag to open_port is used to work around this behaviour in winsock
43  * and the threads doing read and write handle asynchronous I/O.
44  * The ReadFile and WriteFile calls try to cope with both types of I/O, why
45  * the code is not really as Microsoft describes "the right way to do it" in
46  * their documentation. Important to note is that **there is no supported way
47  * to retrieve the information if the HANDLE was opened with
48  * FILE_FLAG_OVERLAPPED from the HANDLE itself**.
49  *
50  */
51 
52 #ifdef HAVE_CONFIG_H
53 #  include "config.h"
54 #endif
55 
56 #include "erl_printf.h"
57 
58 #ifdef WIN32
59 
60 #define WIN32_LEAN_AND_MEAN
61 #include <winsock2.h>
62 #include <windows.h>
63 #include <ws2tcpip.h>
64 #include <process.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 
68 /* These are not used even if they would exist which they should not */
69 #undef HAVE_GETIPNODEBYNAME
70 #undef HAVE_GETHOSTBYNAME2
71 #undef HAVE_GETIPNODEBYADDR
72 
73 #else /* Unix */
74 
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include <unistd.h>
79 #include <stdarg.h>
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 #include <sys/wait.h>
83 #include <netinet/in.h>
84 #include <arpa/inet.h>
85 #include <netdb.h>
86 #include <errno.h>
87 #include <signal.h>
88 
89 #ifdef HAVE_SYS_TIME_H
90 #include <sys/time.h>
91 #else
92 #include <time.h>
93 #endif
94 #include <sys/times.h>
95 
96 #ifndef RETSIGTYPE
97 #define RETSIGTYPE void
98 #endif
99 
100 /* To simplify #ifdef code further down - select only one to be defined...
101 ** Use them in pairs - if one is broken do not trust its mate.
102 **/
103 #if defined(HAVE_GETADDRINFO) && defined(HAVE_GETNAMEINFO)
104 #undef HAVE_GETIPNODEBYNAME
105 #undef HAVE_GETIPNODEBYADDR
106 #undef HAVE_GETHOSTBYNAME2
107 #elif defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_GETIPNODEBYADDR)
108 #undef HAVE_GETADDRINFO
109 #undef HAVE_GETNAMEINFO
110 #undef HAVE_GETHOSTBYNAME2
111 #else
112 #undef HAVE_GETIPNODEBYNAME
113 #undef HAVE_GETIPNODEBYADDR
114 #undef HAVE_GETADDRINFO
115 #undef HAVE_GETNAMEINFO
116 #endif
117 
118 #endif /* !WIN32 */
119 
120 #define PACKET_BYTES 4
121 #ifdef WIN32
122 #define READ_PACKET_BYTES(X,Y,Z) read_int32((X),(Y),(Z))
123 #else
124 #define READ_PACKET_BYTES(X,Y) read_int32((X),(Y))
125 #endif
126 #define PUT_PACKET_BYTES(X,Y) put_int32((X),(Y))
127 /* The serial numbers of the requests */
128 typedef int SerialType;
129 
130 #define INVALID_SERIAL -1
131 
132 /* The operations performed by this program */
133 typedef unsigned char OpType;
134 
135 #define OP_GETHOSTBYNAME 1
136 #define OP_GETHOSTBYADDR 2
137 #define OP_CANCEL_REQUEST 3
138 #define OP_CONTROL 4
139 
140 /* The protocol (IPV4/IPV6) */
141 typedef unsigned char ProtoType;
142 
143 #define PROTO_IPV4 1
144 #define PROTO_IPV6 2
145 
146 /* OP_CONTROL */
147 typedef unsigned char CtlType;
148 #define SETOPT_DEBUG_LEVEL 0
149 
150 /* The unit of an IP address (0 == error, 4 == IPV4, 16 == IPV6) */
151 typedef unsigned char UnitType;
152 
153 #define UNIT_ERROR 0
154 #define UNIT_IPV4  4
155 #define UNIT_IPV6 16
156 
157 /* And the byte type */
158 typedef unsigned char AddrByte; /* Must be compatible with character
159 				   datatype */
160 
161 /*
162  * Marshalled format of request:
163  *{
164  *  Serial: 32 bit big endian
165  *  Op:8 bit  [1,2,3]
166  *  If op == 1 {
167  *    Proto:8 bit [1,2]
168  *    Str: Null terminated array of characters
169  *  } Else if op == 2 {
170  *    Proto:8 bit [1,2]
171  *    If proto == 1 {
172  *      B0..B3: 4 bytes, most significant first
173  *    } Else (proto == 2) {
174  *      B0..B15: 16 bytes, most significant first
175  *    }
176  *  }
177  *  (No more if op == 3)
178  *}
179  * The request arrives as a packet, with 4 packet size bytes.
180  */
181 
182 /* The main process unpackes the marshalled message and sends the data
183  * to a suitable port process or, in the case of a close request, kills the
184  * suitable port process. There is also a que of requests linked together,
185  * for when all subrocesses are busy.
186  */
187 
188 typedef struct QueItem {
189     struct QueItem *next;
190     int req_size;
191     AddrByte request[1];
192 } QueItem; /* Variable size due to request's variable size */
193 
194 QueItem *que_first;
195 QueItem *que_last;
196 
197 #ifdef WIN32
198 typedef struct mesq {
199     HANDLE data_present;
200     CRITICAL_SECTION crit;
201     int shutdown;
202     QueItem *first;
203     QueItem *last;
204 } MesQ;
205 
206 MesQ *to_erlang;
207 MesQ *from_erlang;
208 #endif
209 
210 /*
211  * Marshalled format of reply:
212  *{
213  *  Serial: 32 bit big endian
214  *  Unit: 8 bit, same as h_length or 0 for error
215  *  if unit == 0 {
216  *    Str: Null terminated character string explaining the error
217  *  } else {
218  *    Naddr: 32 bit big endian
219  *    if unit = 4 {
220  *      (B0..B3)0..(B0..B3)Naddr-1: Naddr*4 bytes most significant first
221  *    } else if unit == 16 {
222  *      (B0..B15)0..(B0..B15)Naddr-1: Naddr*16 bytes most significant first
223  *    }
224  *    Nnames: 32 bit big endian >= 1
225  *    Name0: Null terminated string of characters
226  *    Alias[0]..Alias[Nnames - 2]: Nnames - 1 Null terminated strings of chars
227  *  }
228  *}
229  * Four packet size bytes prepended (big endian)
230  */
231 /* Internal error codes */
232 #define ERRCODE_NOTSUP 1
233 #define ERRCODE_HOST_NOT_FOUND 2
234 #define ERRCODE_TRY_AGAIN 3
235 #define ERRCODE_NO_RECOVERY 4
236 #define ERRCODE_NO_DATA 5
237 #define ERRCODE_NETDB_INTERNAL 7
238 
239 /*
240  * Each worker process is represented in the parent by the following struct
241  */
242 
243 typedef unsigned WorkerState;
244 
245 #define WORKER_EMPTY   0 /* No process created       */
246 #define WORKER_FREE    1 /* Living waiting process   */
247 #define WORKER_BUSY    2 /* Living busy process      */
248 #define WORKER_STALLED 3 /* Living cancelled process */
249 
250 /* The timeout when killing a child process in seconds*/
251 #define CHILDWAIT_TMO 1
252 /* The domainname size_limit */
253 #define DOMAINNAME_MAX 258 /* 255 + Opcode + Protocol + Null termination */
254 
255 typedef struct {
256     WorkerState state;
257 #ifdef WIN32
258     DWORD pid; /* 0 if unused */
259     MesQ *writeto; /* Message queues */
260     MesQ *readfrom;
261 #else
262     pid_t pid; /* -1 if unused */
263     int writeto, readfrom; /* Pipes */
264 #endif
265     SerialType serial;
266     AddrByte domain[DOMAINNAME_MAX];
267     QueItem *que_first;
268     QueItem *que_last;
269     int que_size;
270 } Worker;
271 
272 int num_busy_workers;
273 int num_free_workers;
274 int num_stalled_workers;
275 int max_workers;
276 int greedy_threshold;
277 Worker *busy_workers;  /* Workers doing any job that someone really is
278 			  interested in */
279 Worker *free_workers;  /* Really free workers */
280 Worker *stalled_workers; /* May still deliver answers which we will
281 			    discard */
282 #define BEE_GREEDY() (num_busy_workers >= greedy_threshold)
283 
284 static char *program_name;
285 
286 static int debug_level;
287 #ifdef WIN32
288 static HANDLE debug_console_allocated = INVALID_HANDLE_VALUE;
289 #endif
290 
291 #ifdef NODEBUG
292 #define DEBUGF(L,P) /* Nothing */
293 #else
294 #define DEBUGF(Level,Printf) do { if (debug_level >= (Level)) \
295                                       debugf Printf;} while(0)
296 #endif
297 #define ALLOC(Size) my_malloc(Size)
298 #define REALLOC(Old, Size) my_realloc((Old), (Size))
299 #define FREE(Ptr) free(Ptr)
300 
301 #ifdef WIN32
302 #define WAKEUP_WINSOCK() do {			\
303     char dummy_buff[100];			\
304     gethostname(dummy_buff,99);			\
305 } while (0)
306 #endif
307 
308 /* The internal prototypes */
309 static char *format_address(int siz, AddrByte *addr);
310 static void debugf(char *format, ...);
311 static void warning(char *format, ...);
312 static void fatal(char *format, ...);
313 static void *my_malloc(size_t size);
314 static void *my_realloc(void *old, size_t size);
315 static int get_int32(AddrByte *buff);
316 static void put_int32(AddrByte *buff, int value);
317 static int create_worker(Worker *pworker, int save_que);
318 static int map_netdb_error(int netdb_code);
319 #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO)
320 static int map_netdb_error_ai(int netdb_code);
321 #endif
322 static char *errcode_to_string(int errcode);
323 static size_t build_error_reply(SerialType serial, int errnum,
324 				AddrByte **preply,
325 				size_t *preply_size);
326 #ifdef HAVE_GETADDRINFO
327 static size_t build_reply_ai(SerialType serial, int, struct addrinfo *,
328                             AddrByte **preply, size_t *preply_size);
329 #endif
330 static size_t build_reply(SerialType serial, struct hostent *he,
331                          AddrByte **preply, size_t *preply_size);
332 static int read_request(AddrByte **buff, size_t *buff_size);
333 static OpType get_op(AddrByte *buff);
334 static AddrByte *get_op_addr(AddrByte *buff);
335 static SerialType get_serial(AddrByte *buff);
336 static ProtoType get_proto(AddrByte *buff);
337 static CtlType get_ctl(AddrByte *buff);
338 static AddrByte *get_data(AddrByte *buff);
339 static int get_debug_level(AddrByte *buff);
340 static int relay_reply(Worker *pw);
341 static int ignore_reply(Worker *pw);
342 static void init_workers(int max);
343 static void kill_worker(Worker *pw);
344 static Worker *pick_worker(void);
345 static void kill_last_picked_worker(void);
346 static void stall_worker(SerialType serial);
347 static int handle_io_busy(int ndx);
348 static int handle_io_free(int ndx);
349 static int handle_io_stalled(int ndx);
350 static void check_que(void);
351 static void main_loop(void);
352 static void usage(char *unknown);
353 static void domaincopy(AddrByte *out,AddrByte *in);
354 static int domaineq(AddrByte *d1, AddrByte *d2);
355 static int get_domainname(AddrByte *inbuff, int insize, AddrByte *domainbuff);
356 static Worker *pick_worker_greedy(AddrByte *domainbuff);
357 static void restart_worker(Worker *w);
358 static void start_que_request(Worker *w) ;
359 #ifdef WIN32
360 static int read_int32(HANDLE fd, int *res, HANDLE ev);
361 static int read_exact(HANDLE fd, void *vbuff, DWORD nbytes, HANDLE ev);
362 static int write_exact(HANDLE fd, AddrByte *buff, DWORD len,HANDLE ev);
363 DWORD WINAPI worker_loop(void *v);
364 DWORD WINAPI reader(void *data);
365 DWORD WINAPI writer(void *data);
366 static int send_mes_to_worker(QueItem *m, Worker *pw);
367 BOOL create_mesq(MesQ **q);
368 BOOL enque_mesq(MesQ *q, QueItem *m);
369 BOOL deque_mesq(MesQ *q, QueItem **m);
370 BOOL close_mesq(MesQ *q);
371 HANDLE event_mesq(MesQ *q);
372 #else
373 static size_t read_int32(int fd, int *res);
374 static ssize_t read_exact(int fd, void *vbuff, size_t nbytes);
375 static int write_exact(int fd, AddrByte *buff, int len);
376 void reap_children(int ignored);
377 static void init_signals(void);
378 static void kill_all_workers(void);
379 static void close_all_worker_fds(void);
380 static int worker_loop(void);
381 static int fillin_reply(Worker *pw);
382 static int send_request_to_worker(AddrByte *pr, int rsize, Worker *pw);
383 #endif
384 
385 #define ERL_DBG_LVL_ENV_VAR "ERL_INET_GETHOST_DEBUG"
386 
387 static int
get_env_debug_level(void)388 get_env_debug_level(void)
389 {
390 #ifdef __WIN32__
391     char value[21]; /* Enough for any 64-bit values */
392     DWORD sz = GetEnvironmentVariable((LPCTSTR) ERL_DBG_LVL_ENV_VAR,
393 				      (LPTSTR) value,
394 				      (DWORD) sizeof(value));
395     if (sz == 0 || sz > sizeof(value))
396 	return 0;
397 #else
398     char *value = getenv(ERL_DBG_LVL_ENV_VAR);
399     if (!value)
400 	return 0;
401 #endif
402     return atoi(value);
403 }
404 
405 #ifdef WIN32
do_allocate_console(void)406 static void do_allocate_console(void)
407 {
408     AllocConsole();
409     debug_console_allocated = CreateFile ("CONOUT$", GENERIC_WRITE,
410 					  FILE_SHARE_WRITE, NULL,
411 					  OPEN_EXISTING,
412 					  FILE_ATTRIBUTE_NORMAL, NULL);
413 }
414 #ifdef HARDDEBUG
415 DWORD WINAPI pseudo_worker_loop(void *v);
416 static void poll_gethost(int row);
417 #endif
418 #endif
419 
420 /*
421  * Main
422  */
main(int argc,char ** argv)423 int main(int argc, char **argv)
424 {
425     int num_workers = 1;
426     char **ap = argv + 1;
427     int x;
428     int disable_greedy = 0;
429 
430     program_name = *argv;
431     que_first = que_last = NULL;
432     debug_level = get_env_debug_level();
433     greedy_threshold = 0;
434 
435     while (*ap) {
436 	if (!strcmp(*ap, "-d")) {
437 	    ++debug_level;
438 	} else if(!strcmp(*ap, "-g") && *(ap + 1)) {
439 	    ++ap;
440 	    x = atoi(*ap);
441 	    if (!x) {
442 		usage(*ap);
443 	    } else {
444 		greedy_threshold = x;
445 	    }
446 	} else if(!strcmp(*ap, "-ng")) {
447 	    disable_greedy = 1;
448 	} else {
449 	    x = atoi(*ap);
450 	    if (!x) {
451 		usage(*ap);
452 	    } else {
453 		num_workers = x;
454 	    }
455 	}
456 	++ap;
457     }
458 
459 #ifdef WIN32
460     if (num_workers > 60 || greedy_threshold > 60) {
461 	usage("More than 60 workers on windows impossible!");
462 	num_workers = 60;
463 	greedy_threshold = 0;
464     }
465 #endif
466 
467     if(!greedy_threshold) {
468 	greedy_threshold = (3*num_workers)/4; /* 75% */
469 	if (!greedy_threshold) {
470 	    greedy_threshold = num_workers;
471 	}
472     }
473 
474     if (disable_greedy) {
475 	greedy_threshold = num_workers + 1;
476     }
477 
478 #ifdef WIN32
479     {
480 	WORD wr;
481 	WSADATA wsa_data;
482 	int wsa_error;
483 	wr = MAKEWORD(2,0);
484 
485 	wsa_error = WSAStartup(wr,&wsa_data);
486 	if (wsa_error) {
487 	    fatal("Could not open usable winsock library.");
488 	}
489 	if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 0) {
490 	    fatal("Could not open recent enough winsock library.");
491 	}
492 
493 	if (debug_level >= 1) {
494 	    do_allocate_console();
495 
496 	    DEBUGF(1,("num_workers = %d, greedy_threshold = %d, "
497 		      "debug_level = %d.",
498 		      num_workers, greedy_threshold, debug_level));
499 	}
500     }
501     WAKEUP_WINSOCK(); /* Why on earth is this needed? */
502 
503 #endif
504 
505     init_workers(num_workers);
506 
507     main_loop();
508 #ifndef WIN32
509     kill_all_workers();
510 #endif
511     return 0;
512 }
513 
usage(char * unknown)514 static void usage(char *unknown)
515 {
516     fprintf(stderr,"%s: Unknown option \"%s\"\n"
517 	    "Usage: %s [-d [-d ...]] [-g <greedy threshold>] "
518 	    "[<number of workers>]\n",
519 	    program_name, unknown, program_name);
520 }
521 
522 /*
523  * Main process main loop
524  */
525 
handle_io_busy(int ndx)526 static int handle_io_busy(int ndx)
527 {
528     /* Probably an answer */
529     int res;
530     res = relay_reply(&busy_workers[ndx]);
531     if (res < 0) {
532 	/* Bad worker */
533 	if (busy_workers[ndx].que_size) {
534 	    restart_worker(&busy_workers[ndx]);
535 	    start_que_request(&busy_workers[ndx]);
536 	    return 0;
537 	} else {
538 	    kill_worker(&busy_workers[ndx]);
539 	    --num_busy_workers;
540 	    busy_workers[ndx] = busy_workers[num_busy_workers];
541 	}
542 	return 1;
543     } else if (res == 0) {
544 	/* Erlang has closed */
545 	return -1;
546     } else {
547 	if (busy_workers[ndx].que_size) {
548 	   start_que_request(&busy_workers[ndx]);
549 	   return 0;
550 	}
551 	/* The worker is no longer busy, it should be in the free list */
552 	free_workers[num_free_workers] = busy_workers[ndx];
553 	free_workers[num_free_workers].state = WORKER_FREE;
554 	++num_free_workers;
555 	--num_busy_workers;
556 	busy_workers[ndx] = busy_workers[num_busy_workers];
557 	return 1;
558     }
559 }
560 
handle_io_free(int ndx)561 static int handle_io_free(int ndx)
562 {
563     /* IO from a free worker means "kill me" */
564     DEBUGF(1,("Free worker[%ld] spontaneously died.",
565 	      (long) free_workers[ndx].pid));
566     kill_worker(&free_workers[ndx]);
567     --num_free_workers;
568     free_workers[ndx] = free_workers[num_free_workers];
569     return 1;
570 }
571 
handle_io_stalled(int ndx)572 static int handle_io_stalled(int ndx)
573 {
574     int res;
575     res = ignore_reply(&stalled_workers[ndx]);
576     if (res <= 0) {
577 	/* Bad worker */
578 	kill_worker(&stalled_workers[ndx]);
579 	--num_stalled_workers;
580 	stalled_workers[ndx] = stalled_workers[num_stalled_workers];
581 	return 1;
582     } else {
583 	DEBUGF(3,("Ignoring reply from stalled worker[%ld].",
584 		  (long) stalled_workers[ndx].pid));
585 	free_workers[num_free_workers] = stalled_workers[ndx];
586 	free_workers[num_free_workers].state = WORKER_FREE;
587 	++num_free_workers;
588 	--num_stalled_workers;
589 	stalled_workers[ndx] = stalled_workers[num_stalled_workers];
590 	return 1;
591     }
592 }
593 
check_que(void)594 static void check_que(void)
595 {
596     /* Check if anything in the que can be handled */
597     Worker *cw;
598 
599     while (que_first) {
600 	QueItem *qi,*nxt;
601 	qi = que_first;
602 	nxt = qi->next; /* Need to save before it's getting put in another que
603 			   in threaded solution */
604 	if ((cw = pick_worker()) == NULL) {
605 	    break;
606 	}
607 #ifdef WIN32
608 	{
609 	    SerialType save_serial = get_serial(que_first->request);
610 	    if (send_mes_to_worker(que_first, cw) != 0) {
611 		kill_last_picked_worker();
612 		continue;
613 	    }
614 	    cw->serial = save_serial;
615 	}
616 #else
617 	if (send_request_to_worker(que_first->request,
618 				   que_first->req_size, cw) != 0) {
619 	    /* Couldn't send request, kill the worker and retry */
620 	    kill_last_picked_worker();
621 	    continue;
622 	}
623 	cw->serial = get_serial(que_first->request);
624 #endif
625 	/* Went well, lets deque */
626 	que_first = nxt;
627 	if (que_first == NULL) {
628 	    que_last = NULL;
629 	}
630 	DEBUGF(3,("Did deque serial %d, Que is %sempty",
631 		  get_serial(qi->request), (que_first) ? "not " : ""));
632 #ifndef WIN32
633 	FREE(qi);
634 #endif
635     }
636 }
637 
clean_que_of(SerialType s)638 static int clean_que_of(SerialType s)
639 {
640     QueItem **qi;
641     int i;
642 
643     for(qi=&que_first;*qi != NULL &&
644 	    s != get_serial((*qi)->request); qi = &((*qi)->next))
645 	;
646     if(*qi != NULL) {
647 	QueItem *r = *qi;
648 	*qi = (*qi)->next;
649 	FREE(r);
650 	if(que_last == r) {
651 	    /* Lost the "last" pointer, should be very uncommon
652 	       if the que is not empty, so we simply do a traversal
653 	       to reclaim it. */
654 	    if (que_first == NULL) {
655 		que_last = NULL;
656 	    } else {
657 		for (que_last=que_first;que_last->next != NULL;
658 		     que_last = que_last->next)
659 		    ;
660 	    }
661 	}
662 	DEBUGF(3,("Removing serial %d from global que on request, "
663 		  "que %sempty",s, (que_first) ? "not " : ""));
664 	return 1;
665     }
666     for (i = 0; i < num_busy_workers; ++i) {
667 	for(qi=&(busy_workers[i].que_first);*qi != NULL &&
668 		s != get_serial((*qi)->request); qi = &((*qi)->next))
669 	    ;
670 	if(*qi != NULL) {
671 	    QueItem *r = *qi;
672 	    *qi = (*qi)->next;
673 	    FREE(r);
674 	    if(busy_workers[i].que_last == r) {
675 		/* Lost the "last" pointer, should be very uncommon
676 		   if the que is not empty, so we simply do a traversal
677 		   to reclaim it. */
678 		if (busy_workers[i].que_first == NULL) {
679 		    busy_workers[i].que_last = NULL;
680 		    if (busy_workers[i].que_size != 1) {
681 			fatal("Worker que size counter incorrect, internal datastructure error.");
682 		    }
683 		} else {
684 		    for (busy_workers[i].que_last = busy_workers[i].que_first;
685 			 busy_workers[i].que_last->next != NULL;
686 			 busy_workers[i].que_last = busy_workers[i].que_last->next)
687 			;
688 		}
689 	    }
690 	    --(busy_workers[i].que_size);
691 	    DEBUGF(3,("Removing serial %d from worker[%ld] specific que "
692 		      "on request, que %sempty",
693 		      s, (long) busy_workers[i].pid,
694 		      (busy_workers[i].que_first) ? "not " : ""));
695 	    return 1;
696 	}
697     }
698     return 0;
699 }
700 
main_loop(void)701 static void main_loop(void)
702 {
703     AddrByte *inbuff = NULL;
704     int insize;
705     int i,w;
706 #ifdef WIN32
707     HANDLE handles[64];
708     DWORD num_handles;
709     DWORD index;
710     QueItem *qi;
711 #else
712     size_t inbuff_size = 0;
713     fd_set fds;
714     int max_fd;
715 #endif
716     int new_data;
717     int save_serial;
718     /* It's important that the free workers list is handled first */
719     Worker *workers[3] = {free_workers, busy_workers, stalled_workers};
720     int *wsizes[3] = {&num_free_workers, &num_busy_workers,
721 		      &num_stalled_workers};
722     int (*handlers[3])(int) = {&handle_io_free, &handle_io_busy,
723 			       &handle_io_stalled};
724     Worker *cw;
725     AddrByte domainbuff[DOMAINNAME_MAX];
726 
727 #ifdef WIN32
728 
729     {
730 	DWORD dummy;
731 	/* Create the reader and writer */
732 	if ((!create_mesq(&to_erlang)) || (!create_mesq(&from_erlang))) {
733 	    fatal("Could not create message que! errno = %d.",GetLastError());
734 	}
735 	if (((HANDLE) _beginthreadex(NULL,0,writer,to_erlang,0,&dummy))
736 	    == NULL) {
737 	    fatal("Could not create writer thread! errno = %d.",GetLastError());
738 	}
739 	if (((HANDLE) _beginthreadex(NULL,0,reader,from_erlang,0,&dummy))
740 	    == NULL) {
741 	    fatal("Could not create reader thread! errno = %d.",GetLastError());
742 	}
743 	DEBUGF(4,("Created reader and writer threads."));
744 #ifdef HARDDEBUG
745 	poll_gethost(__LINE__);
746 #endif
747     }
748 #endif
749 
750     for(;;) {
751 #ifdef WIN32
752 	num_handles = 0;
753 	handles[num_handles++] = event_mesq(from_erlang);
754 	for (w = 0; w < 3; ++w) {
755 	    for (i = 0; i < *wsizes[w]; ++i) {
756 		handles[num_handles++] = event_mesq(workers[w][i].readfrom);
757 	    }
758 	}
759 
760 	if ((index = WaitForMultipleObjects(num_handles, handles, FALSE, INFINITE))
761 	    == WAIT_FAILED) {
762 	    fatal("Could not WaitForMultpleObjects! errno = %d.",GetLastError());
763 	}
764 	w = 0;
765 	index -= WAIT_OBJECT_0;
766 
767 	DEBUGF(4,("Got data on index %d.",index));
768 	if (index > 0) {
769 	    if (((int)index - 1) < *wsizes[0]) {
770 		(*handlers[0])(index - 1);
771 	    } else if (((int)index - 1) <  ((*wsizes[0]) + (*wsizes[1]))) {
772 		(*handlers[1])(index - 1 - (*wsizes[0]));
773 	    } else {
774 		(*handlers[2])(index - 1 - (*wsizes[0]) - (*wsizes[1]));
775 	    }
776 	}
777 	new_data = (index == 0);
778 #else
779 	max_fd = 0;
780 	FD_ZERO(&fds);
781 	FD_SET(0,&fds);
782 	for (w = 0; w < 3; ++w) {
783 	    for (i = 0; i < *wsizes[w]; ++i) {
784 		FD_SET(workers[w][i].readfrom,&fds);
785 		if (workers[w][i].readfrom > max_fd) {
786 		    max_fd =  workers[w][i].readfrom;
787 		}
788 	    }
789 	}
790 	for (;;) {
791 	    if (select(max_fd + 1,&fds,NULL,NULL,NULL) < 0) {
792 		if (errno == EINTR) {
793 		    continue;
794 		} else {
795 		    fatal("Select failed (invalid internal structures?), "
796 			  "errno = %d.",errno);
797 		}
798 	    }
799 	    break;
800 	}
801 	for (w = 0; w < 3; ++w) {
802 	    for (i = 0; i < *wsizes[w]; ++i) {
803 		if (FD_ISSET(workers[w][i].readfrom, &fds)) {
804 		    int hres = (*handlers[w])(i);
805 		    if (hres < 0) {
806 			return;
807 		    } else {
808 			i -= hres; /* We'll retry this position, if hres == 1.
809 				      The position is usually
810 				      replaced with another worker,
811 				      a worker with
812 				      I/O usually changes state as we
813 				      use blocking file I/O */
814 		    }
815 		}
816 	    }
817 	}
818 	new_data = FD_ISSET(0,&fds);
819 
820 #endif
821 
822 	check_que();
823 
824 	/* Now check for new requests... */
825 	if (new_data) { /* Erlang... */
826 	    OpType op;
827 #ifdef WIN32
828 	    if (!deque_mesq(from_erlang,&qi)) {
829 		DEBUGF(1,("Erlang has closed."));
830 		return;
831 	    }
832 	    insize = qi->req_size;
833 	    inbuff = qi->request;
834 	    DEBUGF(4,("Got data from erlang."));
835 	    DEBUGF(4,("OPeration == %d.",get_op(inbuff)));
836 #else
837 	    insize = read_request(&inbuff, &inbuff_size);
838 	    if (insize == 0) { /* Other errors taken care of in
839 				    read_request */
840 		DEBUGF(1,("Erlang has closed."));
841 		return;
842 	    }
843 #endif
844 	    op = get_op(inbuff);
845 	    if (op == OP_CANCEL_REQUEST) {
846 		SerialType serial = get_serial(inbuff);
847 		if (!clean_que_of(serial)) {
848 		    for (i = 0; i <  num_busy_workers; ++i) {
849 			if (busy_workers[i].serial == serial) {
850 			    if (busy_workers[i].que_size) {
851 				restart_worker(&busy_workers[i]);
852 				start_que_request(&busy_workers[i]);
853 			    } else {
854 				stall_worker(i);
855 				check_que();
856 			    }
857 			    break;
858 			}
859 		    }
860 		}
861 #ifdef WIN32
862 		FREE(qi);
863 #endif
864 		continue; /* New select */
865 	    } else if (op == OP_CONTROL) {
866 		CtlType ctl;
867 		SerialType serial = get_serial(inbuff);
868 		if (serial != INVALID_SERIAL) {
869 		    fatal("Invalid serial: %d.", serial);
870 		}
871 		switch (ctl = get_ctl(inbuff)) {
872 		case SETOPT_DEBUG_LEVEL:
873 		    {
874 			int tmp_debug_level = get_debug_level(inbuff);
875 #ifdef WIN32
876 			if (debug_console_allocated == INVALID_HANDLE_VALUE &&
877 			    tmp_debug_level > 0) {
878 			    DWORD res;
879 			    do_allocate_console();
880 			    WriteFile(debug_console_allocated,
881 				      "Hej\n",4,&res,NULL);
882 			}
883 #endif
884 			debug_level = tmp_debug_level;
885 			DEBUGF(debug_level, ("debug_level = %d", debug_level));
886 			for (w = 0; w < 3; ++w) {
887 			    for (i = 0; i < *wsizes[w]; i++) {
888 				int res;
889 #ifdef WIN32
890 				QueItem *m;
891 #endif
892 				cw = &(workers[w][i]);
893 #ifdef WIN32
894 				m = ALLOC(sizeof(QueItem) - 1 + qi->req_size);
895 				memcpy(m->request, qi->request,
896 				       (m->req_size = qi->req_size));
897 				m->next = NULL;
898 				if ((res = send_mes_to_worker(m, cw)) != 0) {
899 				    FREE(m);
900 				}
901 #else
902 				res = send_request_to_worker(inbuff, insize, cw);
903 #endif
904 				if (res != 0) {
905 				    kill_worker(cw);
906 				    (*wsizes[w])--;
907 				    *cw = workers[w][*wsizes[w]];
908 				}
909 			    }
910 			}
911 		    }
912 	            break;
913 		default:
914 		    warning("Unknown control requested from erlang (%d), "
915 			    "message discarded.", (int) ctl);
916 		    break;
917 		}
918 #ifdef WIN32
919 		FREE(qi);
920 #endif
921 		continue; /* New select */
922 	    } else {
923 		ProtoType proto;
924 		if (op != OP_GETHOSTBYNAME && op != OP_GETHOSTBYADDR) {
925 		    warning("Unknown operation requested from erlang (%d), "
926 			    "message discarded.", op);
927 #ifdef WIN32
928 		    FREE(qi);
929 #endif
930 		    continue;
931 		}
932 		if ((proto = get_proto(inbuff)) != PROTO_IPV4 &&
933 		    proto != PROTO_IPV6) {
934 		    warning("Unknown protocol requested from erlang (%d), "
935 			    "message discarded.", proto);
936 #ifdef WIN32
937 		    FREE(qi);
938 #endif
939 		    continue;
940 		}
941 		if (get_domainname(inbuff,insize,domainbuff) < 0) {
942 		    warning("Malformed message sent from erlang, no domain, "
943 			    "message discarded.", op);
944 #ifdef WIN32
945 		    FREE(qi);
946 #endif
947 		    continue;
948 		}
949 	    }
950 
951 	    if (BEE_GREEDY()) {
952 		DEBUGF(4,("Beeing greedy!"));
953 		if ((cw = pick_worker_greedy(domainbuff)) != NULL) {
954 		    /* Put it in the worker specific que if the
955 		       domainname matches... */
956 #ifndef WIN32
957 		    QueItem *qi = ALLOC(sizeof(QueItem) - 1 +
958 					insize);
959 		    qi->req_size = insize;
960 		    memcpy(&(qi->request), inbuff, insize);
961 		    qi->next = NULL;
962 #endif
963 		    if (!cw->que_first) {
964 			cw->que_first = cw->que_last = qi;
965 		    } else {
966 			cw->que_last->next = qi;
967 			cw->que_last = qi;
968 		    }
969 		    ++(cw->que_size);
970 		    continue;
971 		}
972 		/* Otherwise busyness as usual */
973 	    }
974 
975 	    save_serial = get_serial(inbuff);
976 
977 	    while ((cw = pick_worker()) != NULL) {
978 		int res;
979 #ifdef WIN32
980 		res = send_mes_to_worker(qi,cw);
981 #else
982 		res = send_request_to_worker(inbuff, insize, cw);
983 #endif
984 		if (res == 0) {
985 		    break;
986 		} else {
987 		    kill_last_picked_worker();
988 		}
989 	    }
990 
991 	    if (cw == NULL) {
992 		/* Insert into que */
993 #ifndef WIN32
994 		QueItem *qi = ALLOC(sizeof(QueItem) - 1 +
995 				    insize);
996 		qi->req_size = insize;
997 		memcpy(&(qi->request), inbuff, insize);
998 		qi->next = NULL;
999 #endif
1000 		if (!que_first) {
1001 		    que_first = que_last = qi;
1002 		} else {
1003 		    que_last->next = qi;
1004 		    que_last = qi;
1005 		}
1006 	    } else {
1007 		cw->serial = save_serial;
1008 		domaincopy(cw->domain, domainbuff);
1009 	    }
1010 	}
1011     }
1012 }
1013 
1014 /*
1015  * Main process worker administration
1016  */
1017 
init_workers(int max)1018 static void init_workers(int max)
1019 {
1020     max_workers = max;
1021     num_busy_workers = 0;
1022     num_free_workers = 0;
1023     num_stalled_workers = 0;
1024 
1025     busy_workers = ALLOC(sizeof(Worker) * max_workers);
1026     free_workers = ALLOC(sizeof(Worker) * max_workers);
1027     stalled_workers = ALLOC(sizeof(Worker) * max_workers);
1028 #ifndef WIN32
1029     init_signals();
1030 #endif
1031 }
1032 
1033 #ifdef WIN32
kill_worker(Worker * pw)1034 static void kill_worker(Worker *pw)
1035 {
1036     /* Cannot really kill a thread in win32, have to just leave it to die */
1037     close_mesq(pw->writeto);
1038     close_mesq(pw->readfrom);
1039     pw->state = WORKER_EMPTY;
1040 }
1041 #else
kill_worker(Worker * pw)1042 static void kill_worker(Worker *pw)
1043 {
1044     fd_set fds;
1045     struct timeval tmo;
1046     int selret;
1047     static char buff[1024];
1048 
1049     DEBUGF(3,("Killing worker[%ld] with fd %d, serial %d",
1050 	      (long) pw->pid,
1051 	      (int) pw->readfrom,
1052 	      (int) pw->serial));
1053     kill(pw->pid, SIGUSR1);
1054     /* This is all just to check that the child died, not
1055        really necessary */
1056     for(;;) {
1057 	FD_ZERO(&fds);
1058 	FD_SET(pw->readfrom, &fds);
1059 	tmo.tv_usec=0;
1060 	tmo.tv_sec = CHILDWAIT_TMO;
1061 	selret = select(pw->readfrom+1, &fds, NULL, NULL, &tmo);
1062 	if (selret < 0) {
1063 	    if (errno != EINTR) {
1064 		warning("Unable to select on dying child file descriptor, "
1065 			"errno = %d.",errno);
1066 		break;
1067 	    }
1068 	} else if (selret == 0) {
1069 	    warning("Timeout waiting for child process to die, "
1070 		    "ignoring child (pid = %d).", pw->pid);
1071 	    break;
1072 	} else {
1073 	    int ret;
1074 	    if ((ret = read(pw->readfrom, buff, 1024)) < 0) {
1075 		if (errno != EINTR) {
1076 		    warning("Child file descriptor not closed properly, "
1077 			    "errno = %d", errno);
1078 		    break;
1079 		}
1080 	    } else if (ret == 0) {
1081 		break;
1082 	    }
1083 	    /* continue */
1084 	}
1085     }
1086     /* Waiting is done by signal handler... */
1087     close(pw->readfrom);
1088     close(pw->writeto);
1089     pw->state = WORKER_EMPTY;
1090     /* Leave rest as is... */
1091 }
1092 
kill_all_workers(void)1093 static void kill_all_workers(void)
1094 /* Emergency function, will not check that the children died... */
1095 {
1096     int i;
1097     for (i = 0; i < num_busy_workers; ++i) {
1098 	kill(busy_workers[i].pid, SIGUSR1);
1099     }
1100     for (i = 0; i < num_free_workers; ++i) {
1101 	kill(free_workers[i].pid, SIGUSR1);
1102     }
1103     for (i = 0; i < num_stalled_workers; ++i) {
1104 	kill(stalled_workers[i].pid, SIGUSR1);
1105     }
1106 }
1107 #endif /* !WIN32 */
1108 
pick_worker(void)1109 static Worker *pick_worker(void)
1110 {
1111     Worker tmp;
1112     if (num_free_workers > 0) {
1113 	--num_free_workers;
1114 	tmp = free_workers[num_free_workers];
1115     } else if (num_stalled_workers > 0) {
1116 	/* "restart" the worker... */
1117 	--num_stalled_workers;
1118 	kill_worker(&(stalled_workers[num_stalled_workers]));
1119 	if (create_worker(&tmp,0) < 0) {
1120 	    warning("Unable to create worker process, insufficient "
1121 		    "resources");
1122 	    return NULL;
1123 	}
1124     } else {
1125 	if (num_busy_workers == max_workers) {
1126 	    return NULL;
1127 	}
1128 	if (create_worker(&tmp,0) < 0) {
1129 	    warning("Unable to create worker process, insufficient "
1130 		    "resources");
1131 	    return NULL;
1132 	}
1133     }
1134     /* tmp contains a worker now, make it busy and put it in the right
1135        array */
1136     tmp.state = WORKER_BUSY;
1137     busy_workers[num_busy_workers] = tmp;
1138     ++num_busy_workers;
1139     return &(busy_workers[num_busy_workers-1]);
1140 }
1141 
pick_worker_greedy(AddrByte * domainbuff)1142 static Worker *pick_worker_greedy(AddrByte *domainbuff)
1143 {
1144     int i;
1145     int found = -1;
1146     for (i=0; i < num_busy_workers; ++i) {
1147 	if (domaineq(busy_workers[i].domain, domainbuff)) {
1148 	    if ((found < 0) || (busy_workers[i].que_size <
1149 				busy_workers[found].que_size)) {
1150 		found = i;
1151 	    }
1152 	}
1153     }
1154     if (found >= 0) {
1155 	return &busy_workers[found];
1156     }
1157     return NULL;
1158 }
1159 
restart_worker(Worker * w)1160 static void restart_worker(Worker *w)
1161 {
1162     kill_worker(w);
1163     if (create_worker(w,1) < 0) {
1164 	fatal("Unable to create worker process, insufficient resources");
1165     }
1166 }
1167 
kill_last_picked_worker(void)1168 static void kill_last_picked_worker(void)
1169 {
1170     kill_worker( &(busy_workers[num_busy_workers-1]));
1171     --num_busy_workers;
1172 }
1173 
1174 /*
1175  * Starts a request qued to a specific worker, check_que starts normally queued requests.
1176  * We expect a que here...
1177  */
start_que_request(Worker * w)1178 static void start_que_request(Worker *w)
1179 {
1180     QueItem *qi;
1181     SerialType save_serial;
1182     if (!w->que_first || !w->que_size) {
1183 	fatal("Expected que'd requests but found none, "
1184 	      "internal datastructure corrupted!");
1185     }
1186     qi = w->que_first;
1187     w->que_first = w->que_first->next;
1188     if (!w->que_first) {
1189 	w->que_last = NULL;
1190     }
1191     --(w->que_size);
1192     save_serial = get_serial(qi->request);
1193 #ifdef WIN32
1194     while (send_mes_to_worker(qi, w) != 0) {
1195 	restart_worker(w);
1196     }
1197 #else
1198     while (send_request_to_worker(qi->request,
1199 				  qi->req_size, w) != 0) {
1200 	restart_worker(w);
1201     }
1202 #endif
1203     w->serial = save_serial;
1204     DEBUGF(3,("Did deque serial %d from worker[%ld] specific que, "
1205 	      "Que is %sempty",
1206 	      get_serial(qi->request), (long) w->pid,
1207 	      (w->que_first) ? "not " : ""));
1208 #ifndef WIN32
1209     FREE(qi);
1210 #endif
1211 }
1212 
1213 #ifndef WIN32
1214 /* Signal utilities */
sys_sigset(int sig,RETSIGTYPE (* func)(int))1215 static RETSIGTYPE (*sys_sigset(int sig, RETSIGTYPE (*func)(int)))(int)
1216 {
1217     struct sigaction act, oact;
1218 
1219     sigemptyset(&act.sa_mask);
1220     act.sa_flags = 0;
1221     act.sa_handler = func;
1222     sigaction(sig, &act, &oact);
1223     return(oact.sa_handler);
1224 }
1225 
1226 
sys_sigblock(int sig)1227 static void sys_sigblock(int sig)
1228 {
1229     sigset_t mask;
1230 
1231     sigemptyset(&mask);
1232     sigaddset(&mask, sig);
1233     sigprocmask(SIG_BLOCK, &mask, (sigset_t *)NULL);
1234 }
1235 
sys_sigrelease(int sig)1236 static void sys_sigrelease(int sig)
1237 {
1238     sigset_t mask;
1239 
1240     sigemptyset(&mask);
1241     sigaddset(&mask, sig);
1242     sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL);
1243 }
1244 
1245 /* Child signal handler */
reap_children(int ignored)1246 void reap_children(int ignored)
1247 {
1248     int res;
1249     sys_sigblock(SIGCHLD);
1250     for (;;) {
1251 	while ((res = waitpid((pid_t)-1, NULL, WNOHANG)) > 0)
1252 	    ;
1253 	if (!(res < 0 && errno == EAGAIN)) {
1254 	    DEBUGF(4,("reap_children: res = %d, errno = %d.",res,errno));
1255 	    break;
1256 	}
1257     }
1258     sys_sigrelease(SIGCHLD);
1259 }
1260 
init_signals(void)1261 static void init_signals(void)
1262 {
1263     sys_sigset(SIGCHLD,&reap_children); /* SIG_IGN would give same result
1264 				       on most (?) platforms. */
1265     sys_sigset(SIGPIPE, SIG_IGN);
1266 }
1267 #endif
1268 
stall_worker(int ndx)1269 static void stall_worker(int ndx)
1270 {
1271     --num_busy_workers;
1272     stalled_workers[num_stalled_workers] = busy_workers[ndx];
1273     stalled_workers[num_stalled_workers].state = WORKER_STALLED;
1274     busy_workers[ndx] = busy_workers[num_busy_workers];
1275     DEBUGF(3, ("Stalled worker[%ld]",
1276 	   (long) stalled_workers[num_stalled_workers].pid));
1277     ++num_stalled_workers;
1278 }
1279 
1280 
1281 /*
1282  * Main loop message passing
1283  */
1284 #ifndef WIN32
read_request(AddrByte ** buff,size_t * buff_size)1285 static int read_request(AddrByte **buff, size_t *buff_size)
1286 {
1287     int siz;
1288     int r;
1289 
1290     if ((r = READ_PACKET_BYTES(0,&siz)) != PACKET_BYTES) {
1291 	if (r == 0) {
1292 	    return 0;
1293 	} else {
1294 	    fatal("Unexpected end of file on main input, errno = %d",errno);
1295 	}
1296     }
1297 
1298     if (siz > *buff_size) {
1299 	if (*buff_size == 0) {
1300 	    *buff = ALLOC((*buff_size = siz));
1301 	} else {
1302 	    *buff = REALLOC(*buff, (*buff_size = siz));
1303 	}
1304     }
1305     if (read_exact(0,*buff, siz) != siz) {
1306 	fatal("Unexpected end of file on main input, errno = %d",errno);
1307     }
1308     if (siz < 5) {
1309 	fatal("Unexpected message on main input, message size %d less "
1310 	      "than minimum.");
1311     }
1312     return siz;
1313 }
1314 
1315 #endif /* !WIN32 */
1316 
get_op(AddrByte * buff)1317 static OpType get_op(AddrByte *buff)
1318 {
1319     return (OpType) buff[4];
1320 }
1321 
get_op_addr(AddrByte * buff)1322 static AddrByte *get_op_addr(AddrByte *buff)
1323 {
1324     return  buff + 4;
1325 }
1326 
get_serial(AddrByte * buff)1327 static SerialType get_serial(AddrByte *buff)
1328 {
1329     return get_int32(buff);
1330 }
1331 
get_proto(AddrByte * buff)1332 static ProtoType get_proto(AddrByte *buff)
1333 {
1334     return (ProtoType) buff[5];
1335 }
1336 
get_ctl(AddrByte * buff)1337 static CtlType get_ctl(AddrByte *buff)
1338 {
1339     return (CtlType) buff[5];
1340 }
1341 
get_data(AddrByte * buff)1342 static AddrByte *get_data(AddrByte *buff)
1343 {
1344     return buff + 6;
1345 }
1346 
get_debug_level(AddrByte * buff)1347 static int get_debug_level(AddrByte *buff)
1348 {
1349     return get_int32(buff + 6);
1350 }
1351 
1352 #ifdef WIN32
send_mes_to_worker(QueItem * m,Worker * pw)1353 static int send_mes_to_worker(QueItem *m, Worker *pw)
1354 {
1355     if (!enque_mesq(pw->writeto, m)) {
1356 	warning("Unable to send to child process.");
1357 	return -1;
1358     }
1359     return 0;
1360 }
1361 #else
send_request_to_worker(AddrByte * pr,int rsize,Worker * pw)1362 static int send_request_to_worker(AddrByte *pr, int rsize, Worker *pw)
1363 {
1364     AddrByte hdr[PACKET_BYTES];
1365 
1366     PUT_PACKET_BYTES(hdr, rsize);
1367     if (write_exact(pw->writeto, hdr, PACKET_BYTES) < 0) {
1368 	warning("Unable to write to child process.");
1369 	return -1;
1370     }
1371     if (write_exact(pw->writeto, (AddrByte *) pr, rsize) < 0) {
1372 	warning("Unable to write to child process.");
1373 	return -1;
1374     }
1375     return 0;
1376 }
1377 #endif /* !WIN32 */
1378 
1379 #ifdef WIN32
relay_reply(Worker * pw)1380 static int relay_reply(Worker *pw)
1381 {
1382     QueItem *m;
1383     if (!deque_mesq(pw->readfrom,&m)) {
1384 	return 0;
1385     }
1386     if (!enque_mesq(to_erlang,m)) {
1387 	FREE(m);
1388 	return 0;
1389     }
1390     return 1;
1391 }
1392 
ignore_reply(Worker * pw)1393 static int ignore_reply(Worker *pw) {
1394     QueItem *m;
1395     if (!deque_mesq(pw->readfrom,&m)) {
1396 	return 0;
1397     }
1398     FREE(m);
1399     return 1;
1400 }
1401 
1402 #else
1403 
1404 /* Static buffers used by the next three functions */
1405 static AddrByte *relay_buff = NULL;
1406 static int relay_buff_size = 0;
1407 
fillin_reply(Worker * pw)1408 static int fillin_reply(Worker *pw)
1409 {
1410     int length;
1411 
1412     if (READ_PACKET_BYTES(pw->readfrom, &length) != PACKET_BYTES) {
1413 	warning("Malformed reply (header) from worker process %d.",
1414 		pw->pid);
1415 	return -1;
1416     }
1417 
1418     if (relay_buff_size < (length + PACKET_BYTES)) {
1419 	if (!relay_buff_size) {
1420 	    relay_buff =
1421 		ALLOC((relay_buff_size = (length + PACKET_BYTES)));
1422 	} else {
1423 	    relay_buff =
1424 		REALLOC(relay_buff,
1425 			(relay_buff_size = (length + PACKET_BYTES)));
1426 	}
1427     }
1428     PUT_PACKET_BYTES(relay_buff, length);
1429     if (read_exact(pw->readfrom, relay_buff + PACKET_BYTES, length) !=
1430 	length) {
1431 	warning("Malformed reply (data) from worker process %d.", pw->pid);
1432 	return -1;
1433     }
1434     return length;
1435 }
1436 
relay_reply(Worker * pw)1437 static int relay_reply(Worker *pw)
1438 {
1439     int length = fillin_reply(pw); /* Filled into the "global" buffer */
1440     int res;
1441 
1442     if (length < 0) {
1443 	return -1;
1444     }
1445     if ((res = write_exact(1, relay_buff, length + PACKET_BYTES)) < 0) {
1446 	fatal("Cannot write reply to erlang process, errno = %d.", errno);
1447     } else if (res == 0) {
1448 	DEBUGF(1,("Erlang has closed write pipe."));
1449 	return 0;
1450     }
1451     return length;
1452 }
1453 
ignore_reply(Worker * pw)1454 static int ignore_reply(Worker *pw)
1455 {
1456     return fillin_reply(pw);
1457 }
1458 
1459 #endif /* !WIN32 */
1460 
1461 /*
1462  * Domain name "parsing" and worker specific queing
1463  */
domaincopy(AddrByte * out,AddrByte * in)1464 static void domaincopy(AddrByte *out, AddrByte *in)
1465 {
1466     AddrByte *ptr = out;
1467     *ptr++ = *in++;
1468     *ptr++ = *in++;
1469     switch(*out) {
1470     case OP_GETHOSTBYNAME:
1471 	while(*in != '\0' && *in != '.')
1472 	    ++in;
1473 	strncpy((char*)ptr, (char*)in, DOMAINNAME_MAX-2);
1474 	ptr[DOMAINNAME_MAX-3] = '\0';
1475 	DEBUGF(4,("Saved domainname %s.", ptr));
1476 	return;
1477     case OP_GETHOSTBYADDR:
1478 	memcpy(ptr,in, ((out[1] == PROTO_IPV4) ? UNIT_IPV4 : UNIT_IPV6) - 1);
1479 	DEBUGF(4, ("Saved domain address: %s.",
1480 		   format_address(((out[1] == PROTO_IPV4) ?
1481 				   UNIT_IPV4 : UNIT_IPV6) - 1,ptr)));
1482 	return;
1483     default:
1484 	fatal("Trying to copy buffer not containing valid domain, [%d,%d].",
1485 	      (int) out[0], (int) out[1]);
1486     }
1487 }
1488 
domaineq(AddrByte * d1,AddrByte * d2)1489 static int domaineq(AddrByte *d1, AddrByte *d2)
1490 {
1491     if (d1[0] != d2[0] || d1[1] != d2[1]) {
1492 	return 0;
1493     }
1494     switch (d1[0]) {
1495     case OP_GETHOSTBYNAME:
1496 	return !strcmp((char*)d1+2,(char*)d2+2);
1497     case OP_GETHOSTBYADDR:
1498 	return !memcmp(d1+2,d2+2, ((d1[1] == PROTO_IPV4)
1499 				   ? UNIT_IPV4 : UNIT_IPV6) - 1);
1500     default:
1501 	fatal("Trying to compare buffers not containing valid domain, "
1502 	      "[%d,%d].",
1503 	      (int) d1[0], (int) d1[1]);
1504 	return -1; /* Lint... */
1505     }
1506 }
1507 
get_domainname(AddrByte * inbuff,int insize,AddrByte * domainbuff)1508 static int get_domainname(AddrByte *inbuff, int insize, AddrByte *domainbuff)
1509 {
1510     OpType op = get_op(inbuff);
1511     ProtoType proto;
1512     int i;
1513     AddrByte *data;
1514 
1515     data = get_data(inbuff);
1516     switch (op) {
1517     case OP_GETHOSTBYNAME:
1518 	data = get_data(inbuff);
1519 	for (i = (data - inbuff); i < insize && inbuff[i] != '\0'; ++i)
1520 	    ;
1521 	if (i < insize) {
1522 	    domaincopy(domainbuff, get_op_addr(inbuff));
1523 	    return 0;
1524 	}
1525 	DEBUGF(3, ("Could not pick valid domainname in "
1526 		   "gethostbyname operation"));
1527 	return -1;
1528     case OP_GETHOSTBYADDR:
1529 	proto = get_proto(inbuff);
1530 	i = insize - (data - inbuff);
1531 	if ((proto == PROTO_IPV4 && i == UNIT_IPV4) ||
1532 	    (proto == PROTO_IPV6 && i == UNIT_IPV6)) {
1533 	    /* An address buffer */
1534 	    domaincopy(domainbuff, get_op_addr(inbuff));
1535 	    return 0;
1536 	}
1537 	DEBUGF(3, ("Could not pick valid domainname in gethostbyaddr "
1538 		   "operation"));
1539 	return -1;
1540     default:
1541 	DEBUGF(2, ("Could not pick valid domainname because of "
1542 		   "invalid opcode %d.", (int) op));
1543 	return -1;
1544     }
1545 }
1546 
1547 /*
1548  * Worker subprocesses with utilities
1549  */
1550 #ifdef WIN32
create_worker(Worker * pworker,int save_que)1551 static int create_worker(Worker *pworker, int save_que)
1552 {
1553     MesQ **thread_data = ALLOC(2*sizeof(MesQ *));
1554     DWORD tid;
1555 
1556 
1557     if (!create_mesq(thread_data)) {
1558 	fatal("Could not create, pipes for subprocess, errno = %d",
1559 		GetLastError());
1560     }
1561     if (!create_mesq(thread_data + 1)) {
1562 	fatal("Could not create, pipes for subprocess, errno = %d",
1563 		GetLastError());
1564     }
1565     /* Save those before the thread starts */
1566     pworker->writeto = thread_data[0];
1567     pworker->readfrom = thread_data[1];
1568 
1569     if (((HANDLE) _beginthreadex(NULL, 0, worker_loop, thread_data, 0, &tid))
1570 	== NULL) {
1571 	fatal("Could not create thread errno = %d",
1572 	      GetLastError());
1573     }
1574     pworker->pid = tid;
1575     pworker->state = WORKER_FREE;
1576     pworker->serial = INVALID_SERIAL;
1577     if (!save_que) {
1578 	pworker->que_first = pworker->que_last = NULL;
1579 	pworker->que_size = 0;
1580     }
1581     DEBUGF(3,("Created worker[%ld] with fd %d",
1582 	      (long) pworker->pid, (int) pworker->readfrom));
1583     return 0;
1584 }
1585 
1586 #else
1587 
create_worker(Worker * pworker,int save_que)1588 static int create_worker(Worker *pworker, int save_que)
1589 {
1590     int p0[2], p1[2];
1591     pid_t child;
1592 
1593     if (pipe(p0)) {
1594 	warning("Could not create, pipes for subprocess, errno = %d",
1595 		errno);
1596 	return -1;
1597     }
1598 
1599     if (pipe(p1)) {
1600 	warning("Could not create, pipes for subprocess, errno = %d",
1601 		errno);
1602 	close(p0[0]);
1603 	close(p0[1]);
1604 	return -1;
1605     }
1606     if ((child = fork()) < 0) { /* failure */
1607 	warning("Could not fork(), errno = %d",
1608 		errno);
1609 	close(p0[0]);
1610 	close(p0[1]);
1611 	close(p1[0]);
1612 	close(p1[1]);
1613 	return -1;
1614     } else if (child > 0) { /* parent */
1615 	close(p0[1]);
1616 	close(p1[0]);
1617 	pworker->writeto = p1[1];
1618 	pworker->readfrom = p0[0];
1619 	pworker->pid = child;
1620 	pworker->state = WORKER_FREE;
1621 	pworker->serial = INVALID_SERIAL;
1622 	if (!save_que) {
1623 	    pworker->que_first = pworker->que_last = NULL;
1624 	    pworker->que_size = 0;
1625 	}
1626 	DEBUGF(3,("Created worker[%ld] with fd %d",
1627 		  (long) pworker->pid, (int) pworker->readfrom));
1628 	return 0;
1629     } else { /* child */
1630 	close(p1[1]);
1631 	close(p0[0]);
1632 	close_all_worker_fds();
1633 	/* Make "fatal" not find any children */
1634 	num_busy_workers = num_free_workers = num_stalled_workers = 0;
1635 	if((dup2(p1[0],0) < 0) || (dup2(p0[1],1) < 0)) {
1636 	    fatal("Worker could not dup2(), errno = %d",
1637 		  errno);
1638 	    return -1; /* lint... */
1639 	}
1640 	close(p1[0]);
1641 	close(p0[1]);
1642 	signal(SIGCHLD, SIG_IGN);
1643 	return worker_loop();
1644     }
1645 }
1646 
close_all_worker_fds(void)1647 static void close_all_worker_fds(void)
1648 {
1649     int w,i;
1650     Worker *workers[3] = {free_workers, busy_workers, stalled_workers};
1651     int wsizes[3] = {num_free_workers, num_busy_workers,
1652 		      num_stalled_workers};
1653     for (w = 0; w < 3; ++w) {
1654 	for (i = 0; i < wsizes[w]; ++i) {
1655 	    if (workers[w][i].state != WORKER_EMPTY) {
1656 		close(workers[w][i].readfrom);
1657 		close(workers[w][i].writeto);
1658 	    }
1659 	}
1660     }
1661 }
1662 
1663 #endif /* !WIN32 */
1664 
1665 #ifdef WIN32
worker_loop(void * v)1666 DWORD WINAPI worker_loop(void *v)
1667 #else
1668 static int worker_loop(void)
1669 #endif
1670 {
1671     AddrByte *req = NULL;
1672     size_t req_size = 0;
1673     int this_size;
1674     AddrByte *reply = NULL;
1675     size_t reply_size = 0;
1676     size_t data_size;
1677 
1678 #ifdef WIN32
1679     QueItem *m = NULL;
1680     MesQ *readfrom = ((MesQ **) v)[0];
1681     MesQ *writeto = ((MesQ **) v)[1];
1682     /* XXX:PaN */
1683     FREE(v);
1684 #endif
1685 
1686     for(;;) {
1687 #ifdef HAVE_GETADDRINFO
1688 	struct addrinfo *ai = NULL;
1689 #endif
1690 	struct hostent *he = NULL;
1691 #ifdef HAVE_GETNAMEINFO
1692 	struct sockaddr *sa = NULL;
1693 	char name[NI_MAXHOST];
1694 #endif
1695 #if defined(HAVE_GETIPNODEBYNAME) || defined(HAVE_GETIPNODEBYADDR)
1696 	int free_he = 0;
1697 #endif
1698 	int error_num = 0;
1699 	SerialType serial;
1700 	OpType op;
1701 	ProtoType proto;
1702 	AddrByte *data;
1703 
1704 #ifdef WIN32
1705 	WaitForSingleObject(event_mesq(readfrom),INFINITE);
1706 	DEBUGF(4,("Worker got data on message que."));
1707 
1708 	if(!deque_mesq(readfrom,&m)) {
1709 	    goto fail;
1710 	}
1711 	this_size = m->req_size;
1712 	req = m->request;
1713 #else
1714 	if (READ_PACKET_BYTES(0,&this_size) != PACKET_BYTES) {
1715 	    DEBUGF(2,("Worker got error/EOF while reading size, exiting."));
1716 	    exit(0);
1717 	}
1718 	if (this_size > req_size) {
1719 	    if (req == NULL) {
1720 		req = ALLOC((req_size = this_size));
1721 	    } else {
1722 		req = REALLOC(req, (req_size = this_size));
1723 	    }
1724 	}
1725 	if (read_exact(0, req, (size_t) this_size) != this_size) {
1726 	    DEBUGF(1,("Worker got EOF while reading data, exiting."));
1727 	    exit(0);
1728 	}
1729 #endif
1730 	/* Decode the request... */
1731 	serial = get_serial(req);
1732 	if (OP_CONTROL == (op = get_op(req))) {
1733 	    CtlType ctl;
1734 	    if (serial != INVALID_SERIAL) {
1735 		DEBUGF(1, ("Worker got invalid serial: %d.", serial));
1736 		exit(0);
1737 	    }
1738 	    switch (ctl = get_ctl(req)) {
1739 	    case SETOPT_DEBUG_LEVEL:
1740 		debug_level = get_debug_level(req);
1741 		DEBUGF(debug_level,
1742 		       ("Worker debug_level = %d.", debug_level));
1743 		break;
1744 	    }
1745 	    continue;
1746 	}
1747 	proto = get_proto(req);
1748 	data = get_data(req);
1749 	DEBUGF(4,("Worker got request, op = %d, proto = %d, data = %s.",
1750 		  op,proto,data));
1751 	/* Got a request, lets go... */
1752 	switch (op) {
1753 	case OP_GETHOSTBYNAME:
1754 	    switch (proto) {
1755 
1756 #ifdef HAVE_IN6
1757 	    case PROTO_IPV6: { /* switch (proto) { */
1758 #ifdef HAVE_GETADDRINFO
1759 		struct addrinfo hints;
1760 
1761 		memset(&hints, 0, sizeof(hints));
1762 		hints.ai_flags = AI_CANONNAME;
1763 		hints.ai_socktype = SOCK_STREAM;
1764 		hints.ai_family = AF_INET6;
1765 		DEBUGF(5, ("Starting getaddrinfo(%s, ...)", data));
1766 		error_num = getaddrinfo((char *)data, NULL, &hints, &ai);
1767 		DEBUGF(5,("getaddrinfo returned %d", error_num));
1768 		if (error_num) {
1769 		    error_num = map_netdb_error_ai(error_num);
1770 		}
1771 #elif defined(HAVE_GETIPNODEBYNAME) /*#ifdef HAVE_GETADDRINFO */
1772 		DEBUGF(5,("Starting getipnodebyname(%s)",data));
1773 		he = getipnodebyname(data, AF_INET6, 0, &error_num);
1774 		if (he) {
1775 		    free_he = 1;
1776 		    error_num = 0;
1777 		    DEBUGF(5,("getipnodebyname(,AF_INET6,,) OK"));
1778 		} else {
1779 		    DEBUGF(5,("getipnodebyname(,AF_INET6,,) error %d", error_num));
1780 		    error_num = map_netdb_error(error_num);
1781 		}
1782 #elif defined(HAVE_GETHOSTBYNAME2) /*#ifdef HAVE_GETADDRINFO */
1783 		DEBUGF(5,("Starting gethostbyname2(%s, AF_INET6)",data));
1784 		he = gethostbyname2((char*)data, AF_INET6);
1785 		if (he) {
1786 		    error_num = 0;
1787 		    DEBUGF(5,("gethostbyname2(, AF_INET6) OK"));
1788 		} else {
1789 		    error_num = map_netdb_error(h_errno);
1790 		    DEBUGF(5,("gethostbyname2(, AF_INET6) error %d", h_errno));
1791 		}
1792 #else
1793 		error_num = ERRCODE_NOTSUP;
1794 #endif /*#ifdef HAVE_GETADDRINFO */
1795 	    } break;
1796 #endif /*ifdef HAVE_IN6 */
1797 
1798 	    case PROTO_IPV4: { /* switch (proto) { */
1799 		DEBUGF(5,("Starting gethostbyname(%s)",data));
1800 		he = gethostbyname((char*)data);
1801 		if (he) {
1802 		    error_num = 0;
1803 		    DEBUGF(5,("gethostbyname OK"));
1804 		} else {
1805 		    error_num = map_netdb_error(h_errno);
1806 		    DEBUGF(5,("gethostbyname error %d", h_errno));
1807 		}
1808 	    } break;
1809 
1810 	    default: /* switch (proto) { */
1811 		/* Not supported... */
1812 		error_num = ERRCODE_NOTSUP;
1813 		break;
1814 	    } /* switch (proto) { */
1815 
1816 	    if (he) {
1817 		data_size = build_reply(serial, he, &reply, &reply_size);
1818 #ifdef HAVE_GETIPNODEBYNAME
1819 		if (free_he) {
1820 		    freehostent(he);
1821 		}
1822 #endif
1823 #ifdef HAVE_GETADDRINFO
1824 	    } else if (ai) {
1825 		data_size = build_reply_ai(serial, 16, ai,
1826 					   &reply, &reply_size);
1827 		freeaddrinfo(ai);
1828 #endif
1829 	    } else {
1830 		data_size = build_error_reply(serial, error_num,
1831 					      &reply, &reply_size);
1832 	    }
1833 	    break; /* case OP_GETHOSTBYNAME: */
1834 
1835 	case OP_GETHOSTBYADDR: /* switch (op) { */
1836 	    switch (proto) {
1837 #ifdef HAVE_IN6
1838 	    case PROTO_IPV6: {
1839 #ifdef HAVE_GETNAMEINFO
1840 		struct sockaddr_in6 *sin;
1841 		socklen_t salen = sizeof(*sin);
1842 
1843 		sin = ALLOC(salen);
1844 #ifndef NO_SA_LEN
1845 		sin->sin6_len = salen;
1846 #endif
1847 		sin->sin6_family = AF_INET6;
1848 		sin->sin6_port = 0;
1849 		memcpy(&sin->sin6_addr, data, 16);
1850 		sa = (struct sockaddr *)sin;
1851 		DEBUGF(5,("Starting getnameinfo(,,%s,16,,,)",
1852 			  format_address(16, data)));
1853 		error_num = getnameinfo(sa, salen, name, sizeof(name),
1854 					NULL, 0, NI_NAMEREQD);
1855 		DEBUGF(5,("getnameinfo returned %d", error_num));
1856 		if (error_num) {
1857 		    error_num = map_netdb_error_ai(error_num);
1858 		    sa = NULL;
1859 		}
1860 #elif defined(HAVE_GETIPNODEBYADDR) /*#ifdef HAVE_GETNAMEINFO*/
1861 		struct in6_addr ia;
1862 		memcpy(ia.s6_addr, data, 16);
1863 		DEBUGF(5,("Starting getipnodebyaddr(%s,16,AF_INET6,)",
1864 			  format_address(16, data)));
1865 		he = getipnodebyaddr(&ia, 16, AF_INET6, &error_num);
1866 		free_he = 1;
1867 		if (! he) {
1868 		    DEBUGF(5,("getipnodebyaddr error %d", error_num));
1869 		    error_num = map_netdb_error(error_num);
1870 		} else {
1871 		    DEBUGF(5,("getipnodebyaddr OK"));
1872 		}
1873 #else /*#ifdef HAVE_GETNAMEINFO*/
1874 		struct in6_addr ia;
1875 		memcpy(ia.s6_addr, data, 16);
1876 		DEBUGF(5,("Starting gethostbyaddr(%s,16,AF_INET6)",
1877 			  format_address(16, data)));
1878 		he = gethostbyaddr((const char *) &ia, 16, AF_INET6);
1879 		if (! he) {
1880 		    error_num = map_netdb_error(h_errno);
1881 		    DEBUGF(5,("gethostbyaddr error %d", h_errno));
1882 		} else {
1883 		    DEBUGF(5,("gethostbyaddr OK"));
1884 		}
1885 #endif /* #ifdef HAVE_GETNAMEINFO */
1886 	    } break; /* case PROTO_IPV6: { */
1887 #endif /* #ifdef HAVE_IN6 */
1888 
1889 	    case PROTO_IPV4: { /* switch(proto) { */
1890 		struct in_addr ia;
1891 		memcpy(&ia.s_addr, data, 4); /* Alignment required... */
1892 		DEBUGF(5,("Starting gethostbyaddr(%s,4,AF_INET)",
1893 			  format_address(4, data)));
1894 		he = gethostbyaddr((const char *) &ia, 4, AF_INET);
1895 		if (! he) {
1896 		    error_num = map_netdb_error(h_errno);
1897 		    DEBUGF(5,("gethostbyaddr error %d", h_errno));
1898 		} else {
1899 		    DEBUGF(5,("gethostbyaddr OK"));
1900 		}
1901 	    } break;
1902 
1903 	    default:
1904 		error_num = ERRCODE_NOTSUP;
1905 	    } /* switch(proto) { */
1906 
1907 	    if (he) {
1908 		data_size = build_reply(serial, he, &reply, &reply_size);
1909 #ifdef HAVE_GETIPNODEBYADDR
1910 		if (free_he) {
1911 		    freehostent(he);
1912 		}
1913 #endif
1914 #ifdef HAVE_GETNAMEINFO
1915 	    } else if (sa) {
1916 		struct addrinfo res;
1917 		memset(&res, 0, sizeof(res));
1918 		res.ai_canonname = name;
1919 		res.ai_addr = sa;
1920 		res.ai_next = NULL;
1921 		data_size = build_reply_ai(serial, 16, &res,
1922 					   &reply, &reply_size);
1923 		free(sa);
1924 #endif
1925 	    } else {
1926 		data_size = build_error_reply(serial, error_num,
1927 					      &reply, &reply_size);
1928 	    }
1929 	    break; /* case OP_GETHOSTBYADR: */
1930 
1931 	default:
1932 	    data_size = build_error_reply(serial, ERRCODE_NOTSUP,
1933 					  &reply, &reply_size);
1934 	    break;
1935 	} /* switch (op) { */
1936 
1937 #ifdef WIN32
1938 	m = REALLOC(m, sizeof(QueItem) - 1 + data_size - PACKET_BYTES);
1939 	m->next = NULL;
1940 	m->req_size = data_size - PACKET_BYTES;
1941 	memcpy(m->request,reply + PACKET_BYTES,data_size - PACKET_BYTES);
1942 	if (!enque_mesq(writeto,m)) {
1943 	    goto fail;
1944 	}
1945 	m = NULL;
1946 #else
1947 	/* expect no signals */
1948 	if (write(1, reply, data_size) < 0)
1949 	    goto fail;
1950 #endif
1951     } /* for (;;) */
1952 
1953  fail:
1954 #ifdef WIN32
1955     if (m != NULL) {
1956 	FREE(m);
1957     }
1958     close_mesq(readfrom);
1959     close_mesq(writeto);
1960     if (reply) {
1961 	FREE(reply);
1962     }
1963 #endif
1964     return 1;
1965 }
1966 
map_netdb_error(int netdb_code)1967 static int map_netdb_error(int netdb_code)
1968 {
1969     switch (netdb_code) {
1970 #ifdef HOST_NOT_FOUND
1971     case HOST_NOT_FOUND:
1972 	return ERRCODE_HOST_NOT_FOUND;
1973 #endif
1974 #ifdef TRY_AGAIN
1975     case TRY_AGAIN:
1976 	return ERRCODE_TRY_AGAIN;
1977 #endif
1978 #ifdef NO_RECOVERY
1979     case NO_RECOVERY:
1980 	return ERRCODE_NO_RECOVERY;
1981 #endif
1982 #if defined(NO_DATA) || defined(NO_ADDRESS)
1983 #ifdef NO_DATA
1984     case NO_DATA:
1985 #endif
1986 #ifdef NO_ADDRESS
1987 #if !defined(NO_DATA) || (NO_DATA != NO_ADDRESS)
1988     case NO_ADDRESS:
1989 #endif
1990 #endif
1991 	return ERRCODE_NO_DATA;
1992 #endif
1993     default:
1994 	return ERRCODE_NETDB_INTERNAL;
1995     }
1996 }
1997 
1998 #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO)
map_netdb_error_ai(int netdb_code)1999 static int map_netdb_error_ai(int netdb_code)
2000 {
2001     switch(netdb_code) {
2002 #ifdef EAI_ADDRFAMILY
2003     case EAI_ADDRFAMILY:
2004 	return ERRCODE_NETDB_INTERNAL;
2005 #endif
2006     case EAI_AGAIN:
2007 	return ERRCODE_TRY_AGAIN;
2008     case EAI_BADFLAGS:
2009 	return ERRCODE_NETDB_INTERNAL;
2010     case EAI_FAIL:
2011 	return ERRCODE_HOST_NOT_FOUND;
2012     case EAI_FAMILY:
2013 	return ERRCODE_NETDB_INTERNAL;
2014     case EAI_MEMORY:
2015 	return ERRCODE_NETDB_INTERNAL;
2016 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
2017     case EAI_NODATA:
2018 	return ERRCODE_HOST_NOT_FOUND;
2019 #endif
2020     case EAI_NONAME:
2021 	return ERRCODE_HOST_NOT_FOUND;
2022     case EAI_SERVICE:
2023 	return ERRCODE_NETDB_INTERNAL;
2024     case EAI_SOCKTYPE:
2025 	return ERRCODE_NETDB_INTERNAL;
2026     default:
2027 	return ERRCODE_NETDB_INTERNAL;
2028     }
2029 }
2030 #endif /* #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) */
2031 
2032 
errcode_to_string(int errcode)2033 static char *errcode_to_string(int errcode)
2034 {
2035     switch (errcode) {
2036     case ERRCODE_NOTSUP:
2037 	return "enotsup";
2038     case ERRCODE_HOST_NOT_FOUND:
2039 	/*
2040 	 * I would preffer
2041 	 * return "host_not_found";
2042 	 * but have to keep compatibility with the old
2043 	 * inet_gethost's error codes...
2044 	 */
2045 	return "notfound";
2046     case ERRCODE_TRY_AGAIN:
2047 	return "try_again";
2048     case ERRCODE_NO_RECOVERY:
2049 	return "no_recovery";
2050     case ERRCODE_NO_DATA:
2051 	return "no_data";
2052     default:
2053 	/*case ERRCODE_NETDB_INTERNAL:*/
2054 	return "netdb_internal";
2055     }
2056 }
2057 
build_error_reply(SerialType serial,int errnum,AddrByte ** preply,size_t * preply_size)2058 static size_t build_error_reply(SerialType serial, int errnum,
2059 				AddrByte **preply,
2060 				size_t *preply_size)
2061 {
2062     char *errstring = errcode_to_string(errnum);
2063     int string_need = strlen(errstring) + 1; /* a '\0' too */
2064     unsigned need;
2065     AddrByte *ptr;
2066 
2067     need = PACKET_BYTES + 4 /* Serial */ + 1 /* Unit */ + string_need;
2068     if (*preply_size < need) {
2069 	if (*preply_size == 0) {
2070 	    *preply = ALLOC((*preply_size = need));
2071 	} else {
2072 	    *preply = REALLOC(*preply,
2073 			      (*preply_size = need));
2074 	}
2075     }
2076     ptr = *preply;
2077     PUT_PACKET_BYTES(ptr,need - PACKET_BYTES);
2078     ptr += PACKET_BYTES;
2079     put_int32(ptr,serial);
2080     ptr +=4;
2081     *ptr++ = (AddrByte) 0; /* 4 or 16 */
2082     strcpy((char*)ptr, errstring);
2083     return need;
2084 }
2085 
2086 
2087 
build_reply(SerialType serial,struct hostent * he,AddrByte ** preply,size_t * preply_size)2088 static size_t build_reply(SerialType serial, struct hostent *he,
2089 			  AddrByte **preply, size_t *preply_size)
2090 {
2091     unsigned need;
2092     int strings_need;
2093     int num_strings;
2094     int num_addresses;
2095     int i;
2096     AddrByte *ptr;
2097     int unit = he->h_length;
2098 
2099     for (num_addresses = 0; he->h_addr_list[num_addresses] != NULL;
2100 	 ++num_addresses)
2101 	;
2102     strings_need = strlen(he->h_name) + 1; /* 1 for null byte */
2103     num_strings = 1;
2104     if (he->h_aliases) {
2105 	for(i=0; he->h_aliases[i] != NULL; ++i) {
2106 	    strings_need += strlen(he->h_aliases[i]) + 1;
2107 	    ++num_strings;
2108 	}
2109     }
2110 
2111     need = PACKET_BYTES +
2112 	4 /* Serial */ + 1 /* Unit */ + 4 /* Naddr */ +
2113 	(unit * num_addresses) /* Address bytes */ +
2114 	4 /* Nnames */ + strings_need /* The name and alias strings */;
2115 
2116     if (*preply_size < need) {
2117 	if (*preply_size == 0) {
2118 	    *preply = ALLOC((*preply_size = need));
2119 	} else {
2120 	    *preply = REALLOC(*preply,
2121 			      (*preply_size = need));
2122 	}
2123     }
2124     ptr = *preply;
2125     PUT_PACKET_BYTES(ptr,need - PACKET_BYTES);
2126     ptr += PACKET_BYTES;
2127     put_int32(ptr,serial);
2128     ptr +=4;
2129     *ptr++ = (AddrByte) unit; /* 4 or 16 */
2130     put_int32(ptr, num_addresses);
2131     ptr += 4;
2132     for (i = 0; i < num_addresses; ++i) {
2133 	memcpy(ptr, he->h_addr_list[i], unit);
2134 	ptr += unit;
2135     }
2136     put_int32(ptr, num_strings);
2137     ptr += 4;
2138     strcpy((char*)ptr, he->h_name);
2139     ptr += 1 + strlen(he->h_name);
2140     for (i = 0; i < (num_strings - 1); ++i) {
2141 	strcpy((char*)ptr, he->h_aliases[i]);
2142 	ptr += 1 + strlen(he->h_aliases[i]);
2143     }
2144     return need;
2145 }
2146 
2147 #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO)
build_reply_ai(SerialType serial,int addrlen,struct addrinfo * res0,AddrByte ** preply,size_t * preply_size)2148 static size_t build_reply_ai(SerialType serial, int addrlen,
2149 			     struct addrinfo *res0,
2150 			     AddrByte **preply, size_t *preply_size)
2151 {
2152     struct addrinfo *res;
2153     int num_strings;
2154     int num_addresses;
2155     AddrByte *ptr;
2156     int need;
2157 
2158     num_addresses = 0;
2159     num_strings = 0;
2160     need = PACKET_BYTES +
2161 	4 /* Serial */ + 1 /* addrlen */ +
2162 	4 /* Naddr */ + 4 /* Nnames */;
2163 
2164     for (res = res0; res != NULL; res = res->ai_next) {
2165 	if (res->ai_addr) {
2166 	    num_addresses++;
2167 	    need += addrlen;
2168 	}
2169 	if (res->ai_canonname) {
2170 	    num_strings++;
2171 	    need += strlen(res->ai_canonname) + 1;
2172 	}
2173     }
2174 
2175     if (*preply_size < need) {
2176 	if (*preply_size == 0) {
2177 	    *preply = ALLOC((*preply_size = need));
2178 	} else {
2179 	    *preply = REALLOC(*preply,
2180 			      (*preply_size = need));
2181 	}
2182     }
2183 
2184     ptr = *preply;
2185     PUT_PACKET_BYTES(ptr,need - PACKET_BYTES);
2186     ptr += PACKET_BYTES;
2187     put_int32(ptr,serial);
2188     ptr +=4;
2189     *ptr++ = (AddrByte) addrlen; /* 4 or 16 */
2190     put_int32(ptr, num_addresses);
2191     ptr += 4;
2192     for (res = res0; res != NULL && num_addresses; res = res->ai_next) {
2193 	if (res->ai_addr == NULL)
2194 	    continue;
2195 	if (addrlen == 4)
2196 	    memcpy(ptr, &((struct sockaddr_in *)res->ai_addr)->sin_addr, addrlen);
2197 #ifdef AF_INET6
2198 	else if (addrlen == 16)
2199 	    memcpy(ptr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, addrlen);
2200 #endif
2201 	else
2202 	    memcpy(ptr, res->ai_addr->sa_data, addrlen);
2203 	ptr += addrlen;
2204 	num_addresses--;
2205     }
2206     put_int32(ptr, num_strings);
2207     ptr += 4;
2208     for (res = res0; res != NULL && num_strings; res = res->ai_next) {
2209 	if (res->ai_canonname == NULL)
2210 	    continue;
2211 	strcpy((char *)ptr, res->ai_canonname);
2212 	ptr += strlen(res->ai_canonname) + 1;
2213 	num_strings--;
2214     }
2215     return need;
2216 }
2217 
2218 #endif /* #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) */
2219 
2220 
2221 
2222 /*
2223  * Encode/decode/read/write
2224  */
2225 
get_int32(AddrByte * b)2226 static int get_int32(AddrByte *b)
2227 {
2228     int res;
2229     res = (unsigned) b[3];
2230     res |= ((unsigned) b[2]) << 8;
2231     res |= ((unsigned) b[1]) << 16;
2232     res |= ((unsigned) b[0]) << 24;
2233     return res;
2234 }
2235 
put_int32(AddrByte * buff,int value)2236 static void put_int32(AddrByte *buff, int value)
2237 {
2238     buff[0] = (((unsigned) value) >> 24) & 0xFF;
2239     buff[1] = (((unsigned) value) >> 16) & 0xFF;
2240     buff[2] = (((unsigned) value) >> 8) & 0xFF;
2241     buff[3] = ((unsigned) value) & 0xFF;
2242 }
2243 #ifdef WIN32
2244 
read_int32(HANDLE fd,int * res,HANDLE ev)2245 static int read_int32(HANDLE fd, int *res, HANDLE ev)
2246 {
2247     AddrByte b[4];
2248     int r;
2249     if ((r = read_exact(fd,b,4,ev)) < 0) {
2250 	return -1;
2251     } else if (r == 0) {
2252 	return 0;
2253     } else {
2254 	*res = (unsigned) b[3];
2255 	*res |= ((unsigned) b[2]) << 8;
2256 	*res |= ((unsigned) b[1]) << 16;
2257 	*res |= ((unsigned) b[0]) << 24;
2258     }
2259     return 4;
2260 }
2261 /*
2262  * The standard input is expected to be opened with FILE_FLAG_OVERLAPPED
2263  * but this code should handle both cases (although winsock might not).
2264  */
read_exact(HANDLE fd,void * vbuff,DWORD nbytes,HANDLE ev)2265 static int read_exact(HANDLE fd, void *vbuff, DWORD nbytes, HANDLE ev)
2266 {
2267     DWORD ret,got;
2268     BOOL stat;
2269     char *buff = vbuff;
2270     OVERLAPPED ov;
2271     DWORD err;
2272 
2273 
2274     got = 0;
2275     for(;;) {
2276 	memset(&ov,0,sizeof(ov));
2277 	ov.hEvent = ev;
2278 	ResetEvent(ov.hEvent);
2279 	stat = ReadFile(fd, buff, nbytes - got, &ret, &ov);
2280 	if (!stat) {
2281 	    if ((err = GetLastError()) == ERROR_IO_PENDING) {
2282 		DEBUGF(4,("Overlapped read, waiting for completion..."));
2283 		WaitForSingleObject(ov.hEvent,INFINITE);
2284 		stat = GetOverlappedResult(fd,&ov,&ret,TRUE);
2285 		DEBUGF(4,("Overlapped read, completed with status %d,"
2286 			  " result %d",stat,ret));
2287 	    }
2288 	    if (!stat) {
2289 		if (GetLastError() == ERROR_BROKEN_PIPE) {
2290 		    DEBUGF(1, ("End of file while reading from pipe."));
2291 		    return 0;
2292 		} else {
2293 		    DEBUGF(1, ("Error while reading from pipe,"
2294 			       " errno = %d",
2295 			       GetLastError()));
2296 		    return -1;
2297 		}
2298 	    }
2299 	} else {
2300 	    DEBUGF(4,("Read completed syncronously, result %d",ret));
2301 	}
2302 	if (ret == 0) {
2303 	    DEBUGF(1, ("End of file detected as zero read from pipe."));
2304 	    return 0;
2305 	}
2306 	if (ret < nbytes - got) {
2307 	    DEBUGF(4,("Not all data read from pipe, still %d bytes to read.",
2308 		      nbytes - (got + ret)));
2309 	    got += ret;
2310 	    buff += ret;
2311 	} else {
2312 	    return nbytes;
2313 	}
2314     }
2315 }
2316 /*
2317  * Now, we actually expect a HANDLE opened with FILE_FLAG_OVERLAPPED,
2318  * but this code should handle both cases (although winsock
2319  * does not always..)
2320  */
write_exact(HANDLE fd,AddrByte * buff,DWORD len,HANDLE ev)2321 static int write_exact(HANDLE fd, AddrByte *buff, DWORD len, HANDLE ev)
2322 {
2323     DWORD res,stat;
2324     DWORD x = len;
2325     OVERLAPPED ov;
2326     DWORD err;
2327 
2328 
2329     for(;;) {
2330 	memset(&ov,0,sizeof(ov));
2331 	ov.hEvent = ev;
2332 	ResetEvent(ov.hEvent);
2333 	stat = WriteFile(fd,buff,x,&res,&ov);
2334 	if (!stat) {
2335 	    if ((err = GetLastError()) == ERROR_IO_PENDING) {
2336 		DEBUGF(4,("Overlapped write, waiting for competion..."));
2337 		WaitForSingleObject(ov.hEvent,INFINITE);
2338 		stat = GetOverlappedResult(fd,&ov,&res,TRUE);
2339 		DEBUGF(4,("Overlapped write, completed with status %d,"
2340 			  " result %d",stat,res));
2341 	    }
2342 	    if (!stat) {
2343 		if (GetLastError() == ERROR_BROKEN_PIPE) {
2344 		    return 0;
2345 		} else {
2346 		    return -1;
2347 		}
2348 	    }
2349 	} else {
2350 	    DEBUGF(4,("Write completed syncronously, result %d",res));
2351 	}
2352 
2353 	if (res < x) {
2354 	    /* Microsoft states this can happen as HANDLE is a pipe... */
2355 	    DEBUGF(4,("Not all data written to pipe, still %d bytes to write.",
2356 		      x - res));
2357 	    x -= res;
2358 	    buff += res;
2359 	} else {
2360 	    return len;
2361 	}
2362     }
2363 }
2364 
reader(void * data)2365 DWORD WINAPI reader(void *data) {
2366     MesQ *mq = (MesQ *) data;
2367     QueItem *m;
2368     int siz;
2369     int r;
2370     HANDLE inp;
2371     int x = 0;
2372     HANDLE ev = CreateEvent(NULL, TRUE, FALSE, NULL);
2373 
2374     inp = GetStdHandle(STD_INPUT_HANDLE);
2375     for (;;) {
2376 	if ((r = READ_PACKET_BYTES(inp,&siz,ev)) != 4) {
2377 	    DEBUGF(1,("Erlang has closed (reading)"));
2378 	    exit(0);
2379 	}
2380 	DEBUGF(4,("Read packet of size %d from erlang",siz));
2381 	m = ALLOC(sizeof(QueItem) - 1 + siz);
2382 	if (read_exact(inp, m->request, siz,ev) != siz) {
2383 	    fatal("Unexpected end of file on main input, errno = %d",errno);
2384 	}
2385 	if (siz < 5) {
2386 	    fatal("Unexpected message on main input, message size %d less "
2387 		  "than minimum.");
2388 	}
2389 	m->req_size = siz;
2390 	m->next = NULL;
2391 	if (!enque_mesq(mq, m)) {
2392 	     fatal("Reader could not talk to main thread!");
2393 	}
2394     }
2395 }
2396 
writer(void * data)2397 DWORD WINAPI writer(void *data)
2398 {
2399     MesQ *mq = (MesQ *) data;
2400     QueItem *m;
2401     HANDLE outp = GetStdHandle(STD_OUTPUT_HANDLE);
2402     AddrByte hdr[PACKET_BYTES];
2403     HANDLE ev = CreateEvent(NULL, TRUE, FALSE, NULL);
2404 
2405 
2406     for (;;) {
2407 	WaitForSingleObject(event_mesq(mq),INFINITE);
2408 	if (!deque_mesq(mq, &m)) {
2409 	    fatal("Writer could not talk to main thread!");
2410 	}
2411 	PUT_PACKET_BYTES(hdr, m->req_size);
2412 	if (write_exact(outp, hdr, 4, ev) != 4) {
2413 	    DEBUGF(1,("Erlang has closed (writing)"));
2414 	    exit(0);
2415 	}
2416 	if (write_exact(outp, m->request, m->req_size, ev) != m->req_size) {
2417 	    DEBUGF(1,("Erlang has closed (writing)"));
2418 	    exit(0);
2419 	}
2420 	FREE(m);
2421     }
2422 }
2423 
2424 
2425 #else
2426 
read_int32(int fd,int * res)2427 static size_t read_int32(int fd, int *res)
2428 {
2429     AddrByte b[4];
2430     int r;
2431     if ((r = read_exact(fd,b,4)) < 0) {
2432 	return -1;
2433     } else if (r == 0) {
2434 	return 0;
2435     } else {
2436 	*res = (unsigned) b[3];
2437 	*res |= ((unsigned) b[2]) << 8;
2438 	*res |= ((unsigned) b[1]) << 16;
2439 	*res |= ((unsigned) b[0]) << 24;
2440     }
2441     return 4;
2442 }
2443 
read_exact(int fd,void * vbuff,size_t nbytes)2444 static ssize_t read_exact(int fd, void *vbuff, size_t nbytes)
2445 {
2446     ssize_t ret, got;
2447     char *buff = vbuff;
2448 
2449     got = 0;
2450     for(;;) {
2451 	ret = read(fd, buff, nbytes - got);
2452 	if (ret < 0) {
2453 	    if (errno == EINTR) {
2454 		continue;
2455 	    } else {
2456 		DEBUGF(1, ("Error while reading from pipe,"
2457 			   " errno = %d",
2458 			   errno));
2459 		return -1;
2460 	    }
2461 	} else if (ret == 0) {
2462 	    DEBUGF(1, ("End of file while reading from pipe."));
2463 	    if (got == 0) {
2464 		return 0; /* "Normal" EOF */
2465 	    } else {
2466 		return -1;
2467 	    }
2468 	} else if (ret < nbytes - got) {
2469 	    got += ret;
2470 	    buff += ret;
2471 	} else {
2472 	    return nbytes;
2473 	}
2474     }
2475 }
2476 
write_exact(int fd,AddrByte * buff,int len)2477 static int write_exact(int fd, AddrByte *buff, int len)
2478 {
2479     int res;
2480     int x = len;
2481     for(;;) {
2482 	if((res = write(fd, buff, x)) == x) {
2483 	    break;
2484 	}
2485 	if (res < 0) {
2486 	    if (errno == EINTR) {
2487 		continue;
2488 	    } else if (errno == EPIPE) {
2489 		return 0;
2490 	    }
2491 #ifdef ENXIO
2492 	    else if (errno == ENXIO) {
2493 		return 0;
2494 	    }
2495 #endif
2496 	    else {
2497 		return -1;
2498 	    }
2499 	} else {
2500 	    /* Hmmm, blocking write but not all written, could this happen
2501 	       if the other end was closed during the operation? Well,
2502 	       it costs very little to handle anyway... */
2503 	    x -= res;
2504 	    buff += res;
2505 	}
2506     }
2507     return len;
2508 }
2509 
2510 #endif /* !WIN32 */
2511 
2512 /*
2513  * Debug and memory allocation
2514  */
2515 
format_address(int siz,AddrByte * addr)2516 static char *format_address(int siz, AddrByte *addr)
2517 {
2518     static char buff[50];
2519     char tmp[10];
2520     if (siz > 16) {
2521 	return "(unknown)";
2522     }
2523     *buff='\0';
2524     if (siz <= 4) {
2525 	while(siz--) {
2526 	    erts_snprintf(tmp, sizeof(tmp), "%d",(int) *addr++);
2527 	    strcat(buff,tmp);
2528 	    if(siz) {
2529 		strcat(buff,".");
2530 	    }
2531 	}
2532 	return buff;
2533     }
2534     while(siz--) {
2535 	erts_snprintf(tmp, sizeof(tmp), "%02x",(int) *addr++);
2536 	strcat(buff,tmp);
2537 	if(siz) {
2538 	    strcat(buff,":");
2539 	}
2540     }
2541     return buff;
2542 }
2543 
debugf(char * format,...)2544 static void debugf(char *format, ...)
2545 {
2546     char buff[2048];
2547     char *ptr;
2548     va_list ap;
2549 
2550     va_start(ap,format);
2551 #ifdef WIN32
2552     erts_snprintf(buff, sizeof(buff), "%s[%d] (DEBUG):",program_name,(int) GetCurrentThreadId());
2553 #else
2554     erts_snprintf(buff, sizeof(buff), "%s[%d] (DEBUG):",program_name,(int) getpid());
2555 #endif
2556     ptr = buff + strlen(buff);
2557     erts_vsnprintf(ptr,sizeof(buff)-strlen(buff)-2,format,ap);
2558     strcat(ptr,"\r\n");
2559 #ifdef WIN32
2560     if (debug_console_allocated != INVALID_HANDLE_VALUE) {
2561 	DWORD res;
2562 	WriteFile(debug_console_allocated,buff,strlen(buff),&res,NULL);
2563     }
2564 #else
2565     /* suppress warning with 'if' */
2566     if(write(2,buff,strlen(buff)))
2567 	;
2568 #endif
2569     va_end(ap);
2570 }
2571 
warning(char * format,...)2572 static void warning(char *format, ...)
2573 {
2574     char buff[2048];
2575     char *ptr;
2576     va_list ap;
2577 
2578     va_start(ap,format);
2579     erts_snprintf(buff, sizeof(buff), "%s[%d]: WARNING:",program_name, (int) getpid());
2580     ptr = buff + strlen(buff);
2581     erts_vsnprintf(ptr,sizeof(buff)-strlen(buff)-2,format,ap);
2582     strcat(ptr,"\r\n");
2583 #ifdef WIN32
2584     {
2585 	DWORD res;
2586 	WriteFile(GetStdHandle(STD_ERROR_HANDLE),buff,strlen(buff),&res,NULL);
2587     }
2588 #else
2589     /* suppress warning with 'if' */
2590     if(write(2,buff,strlen(buff)))
2591 	;
2592 #endif
2593     va_end(ap);
2594 }
2595 
fatal(char * format,...)2596 static void fatal(char *format, ...)
2597 {
2598     char buff[2048];
2599     char *ptr;
2600     va_list ap;
2601 
2602     va_start(ap,format);
2603     erts_snprintf(buff, sizeof(buff), "%s[%d]: FATAL ERROR:",program_name, (int) getpid());
2604     ptr = buff + strlen(buff);
2605     erts_vsnprintf(ptr,sizeof(buff)-strlen(buff)-2,format,ap);
2606     strcat(ptr,"\r\n");
2607 #ifdef WIN32
2608     {
2609 	DWORD res;
2610 	WriteFile(GetStdHandle(STD_ERROR_HANDLE),buff,strlen(buff),&res,NULL);
2611     }
2612 #else
2613     /* suppress warning with 'if' */
2614     if(write(2,buff,strlen(buff)))
2615 	;
2616 #endif
2617     va_end(ap);
2618 #ifndef WIN32
2619     kill_all_workers();
2620 #endif
2621     exit(1);
2622 }
2623 
my_malloc(size_t size)2624 static void *my_malloc(size_t size)
2625 {
2626     void *ptr = malloc(size);
2627     if (!ptr) {
2628 	fatal("Cannot allocate %u bytes of memory.", (unsigned) size);
2629 	return NULL; /* lint... */
2630     }
2631     return ptr;
2632 }
2633 
my_realloc(void * old,size_t size)2634 static void *my_realloc(void *old, size_t size)
2635 {
2636     void *ptr = realloc(old, size);
2637     if (!ptr) {
2638 	fatal("Cannot reallocate %u bytes of memory from %p.",
2639 	      (unsigned) size, old);
2640 	return NULL; /* lint... */
2641     }
2642     return ptr;
2643 }
2644 
2645 #ifdef WIN32
2646 
create_mesq(MesQ ** q)2647 BOOL create_mesq(MesQ **q)
2648 {
2649     MesQ *tmp = ALLOC(sizeof(MesQ));
2650     tmp->data_present = CreateEvent(NULL, TRUE, FALSE,NULL);
2651     if (tmp->data_present == NULL) {
2652 	free(tmp);
2653 	return FALSE;
2654     }
2655     InitializeCriticalSection(&(tmp->crit)); /* Cannot fail */
2656     tmp->shutdown = 0;
2657     tmp->first = NULL;
2658     tmp->last = NULL;
2659     *q = tmp;
2660     return TRUE;
2661 }
2662 
enque_mesq(MesQ * q,QueItem * m)2663 BOOL enque_mesq(MesQ *q, QueItem *m)
2664 {
2665     EnterCriticalSection(&(q->crit));
2666     if (q->shutdown) {
2667 	LeaveCriticalSection(&(q->crit));
2668 	return FALSE;
2669     }
2670     if (q->last == NULL) {
2671 	q->first = q->last = m;
2672     } else {
2673 	q->last->next = m;
2674 	q->last = m;
2675     }
2676     m->next = NULL;
2677     if (!SetEvent(q->data_present)) {
2678 	fprintf(stderr,"Fatal: Unable to signal event in %s:%d, last error: %d\n",
2679 		__FILE__,__LINE__,GetLastError());
2680 	exit(1); /* Unable to continue at all */
2681     }
2682     LeaveCriticalSection(&(q->crit));
2683     return TRUE;
2684 }
2685 
deque_mesq(MesQ * q,QueItem ** m)2686 BOOL deque_mesq(MesQ *q, QueItem **m)
2687 {
2688     EnterCriticalSection(&(q->crit));
2689     if (q->first == NULL) { /* Usually shutdown from other end */
2690 	ResetEvent(q->data_present);
2691 	LeaveCriticalSection(&(q->crit));
2692 	return FALSE;
2693     }
2694     *m = q->first;
2695     q->first = q->first->next;
2696     if (q->first == NULL) {
2697 	q->last = NULL;
2698 	ResetEvent(q->data_present);
2699     }
2700     (*m)->next = NULL;
2701     LeaveCriticalSection(&(q->crit));
2702     return TRUE;
2703 }
2704 
close_mesq(MesQ * q)2705 BOOL close_mesq(MesQ *q)
2706 {
2707     QueItem *tmp;
2708     EnterCriticalSection(&(q->crit));
2709     if (!q->shutdown) {
2710 	q->shutdown = TRUE;
2711 	if (!SetEvent(q->data_present)) {
2712 	    fprintf(stderr,
2713 		    "Fatal: Unable to signal event in %s:%d, last error: %d\n",
2714 		    __FILE__,__LINE__,GetLastError());
2715 	    exit(1); /* Unable to continue at all */
2716 	}
2717 	LeaveCriticalSection(&(q->crit));
2718 	return FALSE;
2719     }
2720     /* No one else is supposed to use this object any more */
2721     LeaveCriticalSection(&(q->crit));
2722     DeleteCriticalSection(&(q->crit));
2723     CloseHandle(q->data_present);
2724     tmp = q->first;
2725     while(tmp) {
2726 	q->first = q->first->next;
2727 	free(tmp);
2728 	tmp = q->first;
2729     }
2730     free(q);
2731     return TRUE;
2732 }
2733 
event_mesq(MesQ * q)2734 HANDLE event_mesq(MesQ *q)
2735 {
2736     return q->data_present;
2737 }
2738 
2739 #ifdef HARDDEBUG
pseudo_worker_loop(void * v)2740 DWORD WINAPI pseudo_worker_loop(void *v)
2741 {
2742     HOSTENT *hep;
2743 
2744     DEBUGF(1,("gethostbyname(\"ftp.funet.fi\") starting"));
2745     hep = gethostbyname("ftp.funet.fi");
2746 
2747     DEBUGF(1,("gethostbyname(\"ftp.funet.fi\") -> %d OK",(int) hep));
2748     return 0;
2749 }
2750 
poll_gethost(int row)2751 static void poll_gethost(int row) {
2752     HANDLE h;
2753     DWORD tid;
2754     h = (HANDLE) _beginthreadex(NULL, 0, pseudo_worker_loop, NULL, 0, &tid);
2755     if (h == NULL) {
2756 	DEBUGF(1,("Failed to spawn pseudo worker (%d)...",row));
2757     } else {
2758 	DEBUGF(1,("Waiting for pseudo worker (%d)", row));
2759 	WaitForSingleObject(h,INFINITE);
2760 	DEBUGF(1,("Done Waiting for pseudo worker (%d)", row));
2761     }
2762 }
2763 #endif
2764 
2765 #endif /* WIN32 */
2766