1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 1999-2016. 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 ** This is a trace port, which means it cannot be busy and takes
22 ** no opcodes.
23 ** Send trace messages over the net.
24 */
25 #ifdef HAVE_CONFIG_H
26 #  include "config.h"
27 #endif
28 
29 #ifdef __WIN32__
30 #    include <winsock2.h>
31 #    include <windows.h>
32 #endif
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #ifndef __WIN32__
38 # include <unistd.h>
39 # include <errno.h>
40 # include <sys/types.h>
41 # include <sys/socket.h>
42 # include <netinet/in.h>
43 # include <fcntl.h>
44 #endif
45 
46 #ifdef DEBUG
47 #    include <assert.h>
48 #    define ASSERT(X) assert(X)
49 #else
50 #    define ASSERT(X)
51 #endif
52 
53 #define sock2event(s) ((ErlDrvEvent)(long)(s))
54 #define event2sock(p) ((SOCKET)(long)(p))
55 
56 #include "erl_driver.h"
57 
58 /*
59 ** Protocol from driver:
60 ** '\0' -> ok
61 ** '\1' ++ String -> {error, Atom}
62 ** Protocol when opening (arguments to start):
63 ** <Portno> <Quesize> <Fl>
64 ** Where...
65 ** Portno, ascii string representing a number:
66 **     The TCP port number to listen to.
67 ** Quesize, ascii string representing a number:
68 **     The number of messages to que up before dropping.
69 ** Fl, ascii string representing a flag byte:
70 **     0x1 -> Drop oldest when que is full (instead of last arrived)
71 **     0x2 -> Fill the que even if noone is listening.
72 **
73 ** The package sent over the network looks like this:
74 ** +--+--------+-----------------------------------+
75 ** |Op|Size NBO|Term in external format or empty   |
76 ** +--+--------+-----------------------------------+
77 ** Op, a char:
78 **    0 = binary, 1 = drop
79 **    If Op is 1, then Size reflects the number of dropped messages.
80 ** Size, a 32 bit interger in network byte order:
81 **    Either the size of the binary term, or the number of packet's dropped.
82 ** Term, an array of bytes:
83 **    An erlang term in the external format or simply empty if Op == 1, the
84 **    term is Size long.
85 */
86 
87 /*
88 ** SO, most of the differences between WinDoze and Posixish OS'es
89 ** is handeled here, but the multiplexing (driver_select) is also quite
90 ** interesting, see my_driver_select further down in the file...
91 */
92 
93 #ifndef __WIN32__
94 #    define FLAG_READ ERL_DRV_READ
95 #    define FLAG_WRITE ERL_DRV_WRITE
96      typedef int SOCKET;
97 #    define INVALID_SOCKET -1
98 #    define IS_INVALID_SOCKET(S) ((S) < 0)
99 #    define closesocket(Sock) close(Sock)
100 #    define ERRNO errno
101 #    ifdef EWOULDBLOCK
102 #        define ERRNO_BLOCK EWOULDBLOCK
103 #    else
104 #        ifdef EAGAIN
105 #            define ERRNO_BLOCK EAGAIN
106 #        else
107 #            error "No EWOULDBLOCK found"
108 #        endif
109 #    endif
110 #else /* Win32 */
111 #    define FLAG_READ (FD_READ | FD_CONNECT | FD_ACCEPT)
112 #    define FLAG_WRITE FD_WRITE
113 #    define ERRNO WSAGetLastError()
114 #    define ERRNO_BLOCK WSAEWOULDBLOCK
115 #    define IS_INVALID_SOCKET(S) ((S) == INVALID_SOCKET)
116 #endif
117 
118 
119 /*
120 ** Option flags
121 */
122 #define FLAG_DROP_OLDEST    1
123 #define FLAG_FILL_ALWAYS    2
124 #define FLAG_LISTEN_PORT    4
125 
126 /*
127 ** Op's in messages
128 */
129 #define OP_BINARY 0
130 #define OP_DROP   1
131 
132 /*
133 ** State structure
134 */
135 
136 typedef struct trace_ip_message {
137     int siz; /* the size of the "binary data" */
138     int written; /* if only a part was written, when == siz, all is written */
139     unsigned char bin[1]; /* The opcode, the Size and optionally the binary. */
140 } TraceIpMessage;
141 
142 
143 typedef struct trace_ip_data {
144     unsigned flags;
145     int listen_portno;
146     SOCKET listenfd;
147     SOCKET fd;
148 #ifdef __WIN32__
149     unsigned listen_event_mask;
150     HANDLE listen_event;
151     unsigned event_mask;
152     HANDLE event;
153 #endif
154     ErlDrvPort port;
155     struct trace_ip_data *next;
156     int quesiz;
157     int questart;
158     int questop;
159     TraceIpMessage *que[1]; /* You guessed it, will be longer... */
160 } TraceIpData;
161 
162 static TraceIpData *first_data;
163 
164 /*
165 ** Interface routines
166 */
167 static ErlDrvData trace_ip_start(ErlDrvPort port, char *buff);
168 static void trace_ip_stop(ErlDrvData handle);
169 static void trace_ip_output(ErlDrvData handle, char *buff,
170 			    ErlDrvSizeT bufflen);
171 #ifdef __WIN32__
172 static void trace_ip_event(ErlDrvData handle, ErlDrvEvent event);
173 #endif
174 static void trace_ip_ready_input(ErlDrvData handle, ErlDrvEvent fd);
175 static void trace_ip_ready_output(ErlDrvData handle, ErlDrvEvent fd);
176 static void trace_ip_finish(void); /* No arguments, despite what might be stated
177 				     in any documentation */
178 static ErlDrvSSizeT trace_ip_control(ErlDrvData handle,
179 				     unsigned int command,
180 				     char* buff, ErlDrvSizeT count,
181 				     char** res, ErlDrvSizeT res_size);
182 
183 /*
184 ** Internal routines
185 */
186 static void *my_alloc(size_t size);
187 static ErlDrvBinary *my_alloc_binary(int size);
188 static int write_until_done(SOCKET s, char *buff, int bufflen);
189 static unsigned get_be(unsigned char *s);
190 static void put_be32(unsigned n, unsigned char *s);
191 static void put_be16(unsigned n, unsigned char *s);
192 static TraceIpData *lookup_data_by_port(int portno);
193 static int set_nonblocking(SOCKET sock);
194 static TraceIpMessage *make_buffer(int datasiz, unsigned char op,
195 				   unsigned number);
196 static void enque_message(TraceIpData *data, char *buff, int bufflen,
197 			  int byteswritten);
198 static void clean_que(TraceIpData *data);
199 static void close_client(TraceIpData *data);
200 static int trywrite(TraceIpData *data, char *buff, int bufflen);
201 static SOCKET my_accept(SOCKET sock);
202 static void close_unlink_port(TraceIpData *data);
203 enum MySelectOp { SELECT_ON, SELECT_OFF, SELECT_CLOSE };
204 static int my_driver_select(TraceIpData *desc, SOCKET fd, int flags, enum MySelectOp);
205 static void stop_select(ErlDrvEvent event, void*);
206 
207 /*
208 ** The driver struct
209 */
210 ErlDrvEntry trace_ip_driver_entry = {
211     NULL,	           /* F_PTR init, N/A */
212     trace_ip_start,        /* L_PTR start, called when port is opened */
213     trace_ip_stop,         /* F_PTR stop, called when port is closed */
214     trace_ip_output,       /* F_PTR output, called when erlang has sent */
215 #ifdef __WIN32__
216     trace_ip_event,        /* Anything happens on an associated event */
217     NULL,                  /* Write selections not supported on win32 */
218 #else
219     trace_ip_ready_input,  /* F_PTR ready_input, called when input descriptor
220 			      ready */
221     trace_ip_ready_output, /* F_PTR ready_output, called when output
222 			      descriptor ready */
223 #endif
224     "trace_ip_drv",        /* char *driver_name, the argument to open_port */
225     trace_ip_finish,       /* F_PTR finish, called when unloaded */
226     NULL,                  /* void * that is not used */
227     trace_ip_control,      /* F_PTR control, port_control callback */
228     NULL,                  /* F_PTR timeout, reserved */
229     NULL,                  /* F_PTR outputv, reserved */
230     NULL,                  /* ready_async */
231     NULL,                  /* flush */
232     NULL,                  /* call */
233     NULL,                  /* event */
234     ERL_DRV_EXTENDED_MARKER,
235     ERL_DRV_EXTENDED_MAJOR_VERSION,
236     ERL_DRV_EXTENDED_MINOR_VERSION,
237     0, /* ERL_DRV_FLAGs */
238     NULL,
239     NULL,                  /* process_exit */
240     stop_select
241 };
242 
243 /*
244 ** Driver initialization routine
245 **
246 ** No matter what's stated otherwise, this function shall return a pointer.
247 */
248 
DRIVER_INIT(trace_ip_drv)249 DRIVER_INIT(trace_ip_drv)
250 {
251     first_data = NULL;
252     /*trace_ip_driver_entry.handle = handle; ??? What is this, and why? It is no more! */
253     return &trace_ip_driver_entry;
254 }
255 
256 /*
257 ** Driver interface routines
258 */
259 
260 /*
261 ** Open a port
262 */
trace_ip_start(ErlDrvPort port,char * buff)263 static ErlDrvData trace_ip_start(ErlDrvPort port, char *buff)
264 {
265     TraceIpData *ret;
266     int portno;
267     int quesiz;
268     int flags;
269     SOCKET s;
270     struct sockaddr_in sin;
271     int reuse = 1;
272 
273 #ifdef HARDDEBUG
274     fprintf(stderr,"trace_ip_drv/trace_ip_start (%s)\r\n", buff);
275 #endif
276     if (sscanf(buff,"trace_ip_drv %d %d %d",&portno, &quesiz, &flags) != 3)
277 	return ERL_DRV_ERROR_GENERAL;
278 
279     if (flags > 3 || flags < 0 || portno < 0 || quesiz < 0)
280 	return ERL_DRV_ERROR_GENERAL;
281 
282     if (lookup_data_by_port(portno) != NULL)
283 	return ERL_DRV_ERROR_GENERAL;
284 
285     if (IS_INVALID_SOCKET(s = socket(AF_INET, SOCK_STREAM, 0)))
286 	return ERL_DRV_ERROR_GENERAL;
287 
288     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
289 		   (char *) &reuse, sizeof(reuse)) < 0) {
290 	closesocket(s);
291 	return ERL_DRV_ERROR_GENERAL;
292     }
293 
294 
295     memset(&sin, 0, sizeof(sin));
296     sin.sin_family = AF_INET;
297     sin.sin_addr.s_addr = INADDR_ANY;
298     sin.sin_port = htons((short) portno);
299 
300     if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
301 	closesocket(s);
302 	return ERL_DRV_ERROR_GENERAL;
303     }
304 
305     if (portno == 0) {
306 #ifdef HAVE_SOCKLEN_T
307 	socklen_t sinlen = sizeof(sin);
308 #else
309 	int  sinlen = (int) sizeof(sin);
310 #endif
311 	if (getsockname(s, (struct sockaddr *)&sin, &sinlen) != 0) {
312 	    closesocket(s);
313 	    return ERL_DRV_ERROR_GENERAL;
314 	} else {
315 	    portno = ntohs(sin.sin_port);
316 	}
317     }
318 
319     if (listen(s, 1)) { /* No significant backlog needed */
320 	closesocket(s);
321 	return ERL_DRV_ERROR_GENERAL;
322     }
323 
324     if (set_nonblocking(s) != 0){
325 	closesocket(s);
326 	return ERL_DRV_ERROR_GENERAL;
327     }
328 
329     /* OK, the socket is created, lets build the structure. */
330     /* Deliberately one more pointer than the quesize specified... */
331     ret = my_alloc(sizeof(TraceIpData) +
332 		   quesiz * sizeof(TraceIpMessage *));
333 
334     ret->flags = flags | FLAG_LISTEN_PORT;
335     ret->listen_portno = portno;
336     ret->listenfd = s;
337     ret->fd = INVALID_SOCKET;
338     ret->port = port;
339     ret->next = first_data;
340     ret->quesiz = quesiz+1;
341     ret->questart = ret->questop = 0;
342     memset(ret->que, 0, ret->quesiz);
343 
344     first_data = ret;
345 #ifdef __WIN32__
346     ret->listen_event_mask = 0;
347     ret->listen_event = 0;
348     ret->event_mask = 0;
349     ret->event = 0;
350 #endif
351     my_driver_select(ret, s, FLAG_READ, SELECT_ON);
352     set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
353 
354     return (ErlDrvData) ret;
355 }
356 
357 /*
358 ** Close a port
359 */
trace_ip_stop(ErlDrvData handle)360 static void trace_ip_stop(ErlDrvData handle)
361 {
362     close_unlink_port((TraceIpData *) handle);
363 }
364 
365 /*
366 ** Data sent from erlang to port.
367 */
trace_ip_output(ErlDrvData handle,char * buff,ErlDrvSizeT bufflen)368 static void trace_ip_output(ErlDrvData handle, char *buff, ErlDrvSizeT bufflen)
369 {
370     TraceIpData *data = (TraceIpData *) handle;
371     if (data->flags & FLAG_LISTEN_PORT) {
372 	if (data->flags & FLAG_FILL_ALWAYS) {
373 	    enque_message(data, buff, bufflen, 0);
374 	}
375 	return;
376     }
377     ASSERT(!IS_INVALID_SOCKET(data->fd));
378     if (data->que[data->questart] != NULL) {
379 	trace_ip_ready_output(handle, sock2event(data->fd));
380     }
381     if (data->que[data->questart] == NULL) {
382 	int written = trywrite(data, buff, bufflen);
383 	if (written >= 0 && written < bufflen + 5) {
384 	    enque_message(data, buff, bufflen, written);
385 	    my_driver_select(data, data->fd, FLAG_WRITE, SELECT_ON);
386 	}
387 	return;
388     }
389     enque_message(data, buff, bufflen, 0);
390     return;
391 }
392 
393 /*
394 ** We have something to read from a file descriptor
395 */
trace_ip_ready_input(ErlDrvData handle,ErlDrvEvent fd)396 static void trace_ip_ready_input(ErlDrvData handle, ErlDrvEvent fd)
397 {
398     TraceIpData *data = (TraceIpData *) handle;
399     int client;
400 
401     if (!(data->flags & FLAG_LISTEN_PORT) && event2sock(fd) == data->listenfd) {
402 	/*
403 	** Someone tries to connect to already connected port,
404 	** just accept and close.
405 	*/
406 	if (!IS_INVALID_SOCKET((client = my_accept(data->listenfd)))) {
407 	    closesocket(client);
408 	}
409 	return;
410     }
411 
412     if (event2sock(fd) == data->listenfd) {
413 	/*
414 	** Maybe accept, we are a listen port...
415 	*/
416         ASSERT(IS_INVALID_SOCKET(data->fd));
417 	if (!IS_INVALID_SOCKET((client = my_accept(data->listenfd)))) {
418 	    data->fd = client;
419 	    set_nonblocking(client);
420 	    if (data->que[data->questart] != NULL) {
421 		my_driver_select(data, data->fd, FLAG_WRITE | FLAG_READ, SELECT_ON);
422 	    } else {
423 		my_driver_select(data, data->fd, FLAG_READ, SELECT_ON);
424 	    }
425 	    data->flags &= ~(FLAG_LISTEN_PORT);
426 	}
427 	return;
428     }
429 
430     /*
431      * It is a probably EOF because the other end closed the socket,
432      * but better make sure.
433      */
434 
435     if ((SOCKET)(long)fd == data->fd) {
436 #ifdef __WIN32__
437 	close_client(data);
438 #else
439 	int res;
440 	char sbuf[128];
441 
442 	if ((res = read(data->fd, sbuf, sizeof sbuf)) == 0) {
443 	    close_client(data);
444 	}
445 
446 	/*
447 	 * Something else. Just ignore it.
448 	 *
449 	 * When /dev/poll is used on Solaris, this callback can
450 	 * be called even if there is nothing to read. An attempt
451 	 * to read will result in an EAGAIN error.
452 	 */
453 #ifdef DEBUG
454 	if (res < 0) {
455 	    fprintf(stderr, "Read on fd %d failed with errno=%d\r\n",
456 		    data->fd, errno);
457 	}
458 #endif
459 #endif
460 	return;
461     }
462 
463     ASSERT(0);
464 }
465 
466 #ifdef __WIN32__
trace_ip_event(ErlDrvData handle,ErlDrvEvent event)467 static void trace_ip_event(ErlDrvData handle, ErlDrvEvent event)
468 {
469    TraceIpData *data = (TraceIpData *) handle;
470    if ((HANDLE)event == data->event) {
471        WSANETWORKEVENTS netEv;
472        if (WSAEnumNetworkEvents(data->fd, data->event, &netEv) != 0) {
473 	   return;
474        }
475        if (netEv.lNetworkEvents & FLAG_WRITE) {
476 	   trace_ip_ready_output(handle, (ErlDrvEvent)data->fd);
477        }
478        if (netEv.lNetworkEvents & FLAG_READ) {
479 	   trace_ip_ready_input(handle, (ErlDrvEvent)data->fd);
480        }
481    } else if ((HANDLE)event == data->listen_event) {
482        trace_ip_ready_input(handle, (ErlDrvEvent)data->listenfd);
483    }
484 }
485 #endif /* ifdef __WIN32__ */
486 
487 /*
488 ** We can write to a file descriptor
489 */
trace_ip_ready_output(ErlDrvData handle,ErlDrvEvent fd)490 static void trace_ip_ready_output(ErlDrvData handle, ErlDrvEvent fd)
491 {
492     TraceIpData *data = (TraceIpData *) handle;
493     int res;
494     int towrite;
495     TraceIpMessage *tim;
496 
497     ASSERT(!(data->flags & FLAG_LISTEN_PORT) &&
498 	   data->que[data->questart] != NULL &&
499 	   (SOCKET)(long)fd == data->fd);
500 
501     tim = data->que[data->questart];
502     towrite = tim->siz - tim->written;
503     while((res = write_until_done(data->fd,
504 				  (char *)tim->bin + tim->written, towrite))
505 	  == towrite) {
506 	driver_free(tim);
507 	data->que[data->questart] = NULL;
508 	if (data->questart == data->questop) {
509 	    /*
510 	    ** We wrote the last message in the que, dont
511 	    ** step forward, just 'deselect'
512 	    */
513 	    my_driver_select(data, data->fd, FLAG_WRITE, SELECT_OFF);
514 	    /*
515 	    ** We are really done...
516 	    */
517 	    return;
518 	}
519 	if (++(data->questart) == data->quesiz)
520 	    data->questart = 0;
521 	tim = data->que[data->questart];
522 	ASSERT(tim != NULL);
523 	towrite = tim->siz - tim->written;
524     }
525     if (res < 0) {
526 	close_client(data);
527 	return;
528     }
529 
530     tim->written += res;
531 }
532 
533 /*
534 ** Control message from erlang, we handle $p, which is get_listen_port.
535 */
trace_ip_control(ErlDrvData handle,unsigned int command,char * buff,ErlDrvSizeT count,char ** res,ErlDrvSizeT res_size)536 static ErlDrvSSizeT trace_ip_control(ErlDrvData handle,
537 				     unsigned int command,
538 				     char* buff, ErlDrvSizeT count,
539 				     char** res, ErlDrvSizeT res_size)
540 {
541     register void *void_ptr; /* Soft type cast */
542 
543     if (command == 'p') {
544 	TraceIpData *data = (TraceIpData *) handle;
545 	ErlDrvBinary *b = my_alloc_binary(3);
546 	b->orig_bytes[0] = '\0'; /* OK */
547 	put_be16(data->listen_portno, (unsigned char *)&(b->orig_bytes[1]));
548 	*res = void_ptr = b;
549 	return 0;
550     }
551     return -1;
552 }
553 
554 /*
555 ** Driver unloaded
556 */
trace_ip_finish(void)557 static void trace_ip_finish(void)
558 {
559     while (first_data != NULL) {
560 	close_unlink_port(first_data);
561     }
562 }
563 
564 /*
565 ** Internal helpers
566 */
567 
568 /*
569 ** Yet another malloc wrapper
570 */
my_alloc(size_t size)571 static void *my_alloc(size_t size)
572 {
573     void *ret;
574     if ((ret = driver_alloc(size)) == NULL) {
575 	/* May or may not work... */
576 	fprintf(stderr, "Could not allocate %lu bytes of memory in %s.",
577 		(unsigned long) size, __FILE__);
578 	exit(1);
579     }
580     return ret;
581 }
582 
583 /*
584 ** Yet another malloc wrapper
585 */
my_alloc_binary(int size)586 static ErlDrvBinary *my_alloc_binary(int size)
587 {
588     ErlDrvBinary *ret;
589     if ((ret = driver_alloc_binary(size)) == NULL) {
590 	/* May or may not work... */
591 	fprintf(stderr, "Could not allocate a binary of %lu bytes in %s.",
592 		(unsigned long) size, __FILE__);
593 	exit(1);
594     }
595     return ret;
596 }
597 
598 /*
599 ** Write to a nonblocking descriptor until it states EWOULDBLOCK
600 */
write_until_done(SOCKET s,char * buff,int bufflen)601 static int write_until_done(SOCKET s, char *buff, int bufflen)
602 {
603     int ret = 0;
604     int res = 0;
605 
606 #ifdef HARDDEBUG
607     fprintf(stderr, "Writing %d characters.\r\n", bufflen);
608 #endif
609 
610     while(ret < bufflen && (res = send(s, buff + ret, bufflen - ret, 0)) > 0) {
611 	ret += res;
612     }
613     if (ret < bufflen) {
614 	if (res == 0) {
615 	    fprintf(stderr, "internal error in trace_ip_drv, "
616 		     "write to nonblocking "
617 		     "returned 0!");
618 	    exit(1);
619 	} else if (ERRNO != ERRNO_BLOCK) {
620 		return -1;
621 	}
622     }
623     return ret;
624 }
625 
626 /*
627 ** Translate big endian integer in buffer to unsigned
628 */
get_be(unsigned char * s)629 static unsigned get_be(unsigned char *s)
630 {
631     return s[3] | (s[2] << 8) | (s[1] << 16) | s[0] << 24;
632 }
633 
634 /*
635 ** Write unsigned to buffer in big endian
636 */
637 
put_be32(unsigned n,unsigned char * s)638 static void put_be32(unsigned n, unsigned char *s)
639 {
640     s[0] = n >> 24U;
641     s[1] = n >> 16U;
642     s[2] = n >> 8U;
643     s[3] = n;
644 }
645 
put_be16(unsigned n,unsigned char * s)646 static void put_be16(unsigned n, unsigned char *s)
647 {
648     s[0] = n >> 8U;
649     s[1] = n;
650 }
651 
652 /*
653 ** Lookup a port's data structure by the *TCP/IP port number*
654 */
lookup_data_by_port(int portno)655 static TraceIpData *lookup_data_by_port(int portno)
656 {
657     TraceIpData *tmp = first_data;
658     while (tmp != NULL && tmp->listen_portno != portno)
659 	tmp = tmp->next;
660     return tmp;
661 }
662 
663 /*
664 ** Create a TraceIpMessage buffer (the binary data NOT filled in)
665 */
make_buffer(int datasiz,unsigned char op,unsigned number)666 static TraceIpMessage *make_buffer(int datasiz, unsigned char op,
667 				   unsigned number)
668 {
669     TraceIpMessage *ret = my_alloc(sizeof(TraceIpMessage) +
670 				   (datasiz + 4));
671     ret->siz = datasiz + 5;
672     ret->written = 0;
673     ret->bin[0] = op;
674     put_be32(number, ret->bin + 1);
675     return ret;
676 }
677 
678 /*
679 ** Add message to que, discarding in a politically correct way...
680 ** The FLAG_DROP_OLDEST is currently ingored...
681 */
enque_message(TraceIpData * data,char * buff,int bufflen,int byteswritten)682 static void enque_message(TraceIpData *data, char *buff, int bufflen,
683 			  int byteswritten)
684 {
685     int diff = data->questop - data->questart;
686     TraceIpMessage *tim;
687 
688     if (diff == -1 || diff == data->quesiz - 1) {
689 	put_be32(get_be((data->que[data->questop])->bin + 1) + 1,
690 		 (data->que[data->questop])->bin + 1);
691     } else if (diff == -2 || diff == data->quesiz - 2) {
692 	ASSERT(byteswritten == 0);
693 	if (++(data->questop) ==  data->quesiz) {
694 	    data->questop = 0;
695 	}
696 	data->que[data->questop] = make_buffer(0, OP_DROP, 1);
697     } else {
698 	if (data->que[data->questop] != NULL &&
699 	    ++(data->questop) ==  data->quesiz) {
700 	    data->questop = 0;
701 	}
702 	tim = make_buffer(bufflen, OP_BINARY, bufflen);
703 	tim->written = byteswritten;
704 	memcpy(tim->bin + 5, buff, bufflen);
705 	data->que[data->questop] = tim;
706     }
707 }
708 
709 /*
710 ** Clean a que
711 */
clean_que(TraceIpData * data)712 static void clean_que(TraceIpData *data)
713 {
714     int b = data->questart;
715     int e = data->questop;
716 
717     while (b != e) {
718 	if (data->que[b] != NULL) {
719 	    driver_free(data->que[b]);
720 	    data->que[b] = NULL;
721 	}
722 	if (++b >= data->quesiz) {
723 	    b = 0;
724 	}
725     }
726     if (data->que[b] != NULL) {
727 	driver_free(data->que[b]);
728 	data->que[b] = NULL;
729     }
730     data->questart = data->questop = 0;
731 }
732 
733 /*
734 ** Cleanup closed client (or close the client and cleanup)
735 */
close_client(TraceIpData * data)736 static void close_client(TraceIpData *data)
737 {
738     my_driver_select(data, data->fd, FLAG_WRITE | FLAG_READ, SELECT_CLOSE);
739     data->flags |= FLAG_LISTEN_PORT;
740     data->fd = INVALID_SOCKET;
741     if (!(data->flags & FLAG_FILL_ALWAYS)) {
742 	clean_que(data);
743     }
744 }
745 
746 /*
747 ** Try to write a message from erlang directly (only called when que is empty
748 ** and client is connected)
749 */
trywrite(TraceIpData * data,char * buff,int bufflen)750 static int trywrite(TraceIpData *data, char *buff, int bufflen)
751 {
752     char op[5];
753     int res;
754 
755     op[0] = OP_BINARY;
756     put_be32(bufflen, (unsigned char *)op + 1);
757 
758     if ((res = write_until_done(data->fd, op, 5)) < 0) {
759 	close_client(data);
760 	return -1;
761     }
762     if (res < 5) {
763 	return res;
764     }
765 
766     if ((res = write_until_done(data->fd, buff, bufflen)) < 0) {
767 	close_client(data);
768 	return -1;
769     }
770 
771     return res + 5;
772 }
773 
774 /*
775 ** accept wrapper
776 */
my_accept(SOCKET sock)777 static SOCKET my_accept(SOCKET sock)
778 {
779     struct sockaddr_in sin;
780 #ifdef HAVE_SOCKLEN_T
781     socklen_t sin_size = sizeof(sin);
782 #else
783     int sin_size = (int) sizeof(sin);
784 #endif
785 
786     return accept(sock, (struct sockaddr *) &sin, &sin_size);
787 }
788 
789 /*
790 ** Close the whole port and clean up
791 */
close_unlink_port(TraceIpData * data)792 static void close_unlink_port(TraceIpData *data)
793 {
794     TraceIpData **tmp;
795 
796     data->flags = 0;
797     if (!IS_INVALID_SOCKET(data->fd)) {
798 	close_client(data);
799     }
800     my_driver_select(data, data->listenfd, FLAG_READ, SELECT_CLOSE);
801 
802     for(tmp = &first_data; *tmp != NULL && *tmp != data;
803 	tmp = &((*tmp)->next))
804 	;
805     if (*tmp != NULL) {
806 	*tmp = (*tmp)->next;
807     }
808     driver_free(data);
809 }
810 
811 
812 
813 
814 #ifdef __WIN32__
815 /*
816 ** Mostly stolen from inet_drv in the emulator.
817 */
my_driver_select(TraceIpData * desc,SOCKET fd,int flags,enum MySelectOp op)818 static int my_driver_select(TraceIpData *desc, SOCKET fd,
819 			    int flags, enum MySelectOp op)
820 {
821     HANDLE *event;
822     unsigned *event_mask;
823     int ret = -1;
824     unsigned save_event_mask;
825 
826     if(fd == desc->listenfd) {
827 	event = &(desc->listen_event);
828 	event_mask = &(desc->listen_event_mask);
829     } else if(fd == desc->fd) {
830 	event = &(desc->event);
831 	event_mask = &(desc->event_mask);
832     } else {
833 	return -1;
834     }
835 
836     save_event_mask = *event_mask;
837 
838     if (op==SELECT_ON) {
839 	*event_mask |= flags;
840     } else {
841 	*event_mask &= (~flags);
842     }
843     if (*event_mask != 0 && *event == 0) {
844 	*event = WSACreateEvent();
845 	driver_select(desc->port, *event, ERL_DRV_READ|ERL_DRV_USE, 1);
846     }
847 
848     /* The RIGHT WAY (TM) to do this is to make sure:
849        A) The cancelling of all network events is done with
850           NULL as the event parameter (bug in NT's winsock),
851        B) The actual event handle is reset so that it is only
852           raised if one of the requested network events is active,
853        C) Avoid race conditions by making sure that the event cannot be set
854           while we are preparing to set the correct network event mask.
855        The simplest way to do it is to turn off all events, reset the
856        event handle and then, if event_mask != 0, turn on the appropriate
857        events again. */
858     if (WSAEventSelect(fd, NULL, 0) != 0) {
859 	*event_mask = save_event_mask;
860 	goto error;
861     }
862     if (!ResetEvent(*event)) {
863 	*event_mask = 0;
864 	goto error;
865     }
866     if (*event_mask != 0) {
867 	if (WSAEventSelect(fd,
868 			   *event,
869 			   *event_mask) != 0) {
870 	    *event_mask = 0;
871 	    goto error;
872 	}
873     }
874     ret = 0;
875 error:
876     if (*event_mask == 0 && *event != 0 && (op==SELECT_CLOSE || ret!=0)) {
877 	WSAEventSelect(fd, NULL, 0); /* Not necessary?
878 					Well, actually I dont know, and
879 					MS documentation states nothing
880 					about what happens if WSAEventSelect
881 					is called with empty event mask and
882 					then the event is deleted. */
883 	driver_select(desc->port, *event, ERL_DRV_READ|ERL_DRV_USE, 0);
884 	*event = 0;
885 	if (op == SELECT_CLOSE) {
886 	    closesocket(fd);
887 	}
888     }
889     return ret;
890 }
891 
stop_select(ErlDrvEvent event,void * _)892 static void stop_select(ErlDrvEvent event, void* _)
893 {
894     WSACloseEvent((HANDLE)event);
895 }
896 
897 #else /* UNIX */
898 
my_driver_select(TraceIpData * desc,SOCKET fd,int flags,enum MySelectOp op)899 static int my_driver_select(TraceIpData *desc, SOCKET fd, int flags, enum MySelectOp op)
900 {
901     if (op != SELECT_OFF) {
902 	flags |= ERL_DRV_USE;
903     }
904     return driver_select(desc->port, sock2event(fd), flags, (op==SELECT_ON));
905 }
906 
stop_select(ErlDrvEvent event,void * _)907 static void stop_select(ErlDrvEvent event, void* _)
908 {
909     closesocket((SOCKET)(long)event);
910 }
911 
912 #endif /* !__WIN32__ */
913 
914 /*
915 ** Set socket nonblocking, keep this at end of file.
916 */
917 #undef ERRNO_BLOCK
918 #undef ASSERT
919 #ifndef WANT_NONBLOCKING
920 #define WANT_NONBLOCKING
921 #endif
922 #include "sys.h"
set_nonblocking(SOCKET sock)923 static int set_nonblocking(SOCKET sock)
924 {
925     SET_NONBLOCKING(sock);
926     return 0;
927 }
928 
929 
930