/
afunix_drv.c
5725 lines (5060 loc) · 160 KB
/
afunix_drv.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 1997-2012. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* %CopyrightEnd%
*/
#ifdef __linux__
#define __USE_GNU
#define _GNU_SOURCE
#define NO_SA_LEN
#endif
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdint.h>
#include <memory.h>
#include <ctype.h>
#include <math.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/uio.h>
#define IDENTITY(c) c
#define STRINGIFY_1(b) IDENTITY(#b)
#define STRINGIFY(a) STRINGIFY_1(a)
/* All platforms fail on malloc errors. */
#define FATAL_MALLOC
#include "erl_driver.h"
/* The IS_SOCKET_ERROR macro below is used for portability reasons. While
POSIX specifies that errors from socket-related system calls should be
indicated with a -1 return value, some users have experienced non-Windows
OS kernels that return negative values other than -1. While one can argue
that such kernels are technically broken, comparing against values less
than 0 covers their out-of-spec return values without imposing incorrect
semantics on systems that manage to correctly return -1 for errors, thus
increasing Erlang's portability.
*/
#define IS_SOCKET_ERROR(val) ((val) < 0)
#define LLU "%llu"
typedef unsigned long long llu_t;
#include <sys/time.h>
#ifdef NETDB_H_NEEDS_IN_H
#include <netinet/in.h>
#endif
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H
#include <rpc/types.h>
#endif
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/param.h>
#ifdef HAVE_ARPA_NAMESER_H
#include <arpa/nameser.h>
#endif
#ifdef HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif
#include <sys/ioctl.h>
#include <sys/un.h>
#if defined (__APPLE__) || defined (__FreeBSD__)
#define HAVE_SUN_LEN_FIELD
#include <sys/ucred.h>
#endif
#if !defined(SOL_LOCAL)
#define SOL_LOCAL 0
#endif
#if defined (__SVR4) && defined (__sun)
#define NO_SA_LEN
#define HAVE_GETPEERUCRED
#include <limits.h>
#include <string.h>
#include <ucred.h>
#endif
// testing #undef LOCAL_PEERCRED
// testing #undef LOCAL_PEERPID
// missing LOCAL_PEERCRED in sys/un.h probably a 10.7.x and earlier
#if defined (__APPLE__) && !defined(LOCAL_PEERCRED)
#define HAVE_GETPEEREID
#endif
// FIXME use dlib!!!
#define DLOG_DEBUG 7
#define DLOG_INFO 6
#define DLOG_NOTICE 5
#define DLOG_WARNING 4
#define DLOG_ERROR 3
#define DLOG_CRITICAL 2
#define DLOG_ALERT 1
#define DLOG_EMERGENCY 0
#define DLOG_NONE -1
#ifndef DLOG_DEFAULT
#define DLOG_DEFAULT DLOG_NONE
#endif
#define DLOG(level,file,line,args...) do { \
if (((level) == DLOG_EMERGENCY) || \
((debug_level >= 0) && ((level) <= debug_level))) { \
emit_log((level),(file),(line),args); \
} \
} while(0)
#define DEBUGF(args...) DLOG(DLOG_DEBUG,__FILE__,__LINE__,args)
#define INFOF(args...) DLOG(DLOG_INFO,__FILE__,__LINE__,args)
#define NOTICEF(args...) DLOG(DLOG_NOTICE,__FILE__,__LINE__,args)
#define WARNINGF(args...) DLOG(DLOG_WARNING,__FILE__,__LINE__,args)
#define ERRORF(args...) DLOG(DLOG_ERROR,__FILE__,__LINE__,args)
#define CRITICALF(args...) DLOG(DLOG_CRITICAL,__FILE__,__LINE__,args)
#define ALERTF(args...) DLOG(DLOG_ALERT,__FILE__,__LINE__,args)
#define EMERGENCYF(args...) DLOG(DLOG_EMERGENCY,__FILE__,__LINE__,args)
#define INVALID_SOCKET -1
#define INVALID_EVENT -1
#define SOCKET_ERROR -1
#define SOCKET int
#define HANDLE long int
#define FD_READ ERL_DRV_READ
#define FD_WRITE ERL_DRV_WRITE
#define FD_CLOSE 0
#define FD_CONNECT ERL_DRV_WRITE
#define FD_ACCEPT ERL_DRV_READ
#define sock_connect(s, addr, len) connect((s), (addr), (len))
#define sock_listen(s, b) listen((s), (b))
#define sock_bind(s, addr, len) bind((s), (addr), (len))
#define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l))
#define sock_setopt(s,t,n,v,l) setsockopt((s),(t),(n),(v),(l))
#define sock_name(s, addr, len) getsockname((s), (addr), (len))
#define sock_peer(s, addr, len) getpeername((s), (addr), (len))
#define sock_ntohs(x) ntohs((x))
#define sock_ntohl(x) ntohl((x))
#define sock_htons(x) htons((x))
#define sock_htonl(x) htonl((x))
#define sock_accept(s, addr, len) accept((s), (addr), (len))
#define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag))
#define sock_sendto(s,buf,blen,flag,addr,alen) \
sendto((s),(buf),(blen),(flag),(addr),(alen))
#define sock_sendv(s, vec, size, np, flag) \
(*(np) = writev((s), (struct iovec*)(vec), (size)))
#define sock_sendmsg(s,msghdr,flag) sendmsg((s),(msghdr),(flag))
#define sock_open(af, type, proto) socket((af), (type), (proto))
#define sock_close(s) close((s))
#define sock_shutdown(s, how) shutdown((s), (how))
#define sock_hostname(buf, len) gethostname((buf), (len))
#define sock_getservbyname(name,proto) getservbyname((name), (proto))
#define sock_getservbyport(port,proto) getservbyport((port), (proto))
#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag))
#define sock_recvfrom(s,buf,blen,flag,addr,alen) \
recvfrom((s),(buf),(blen),(flag),(addr),(alen))
#define sock_recvmsg(s,msghdr,flag) recvmsg((s),(msghdr),(flag))
#define sock_errno() errno
#define sock_create_event(d) ((d)->s) /* return file descriptor */
#define sock_close_event(e) /* do nothing */
#define inet_driver_select(port, e, mode, on) \
driver_select(port, e, mode | (on?ERL_DRV_USE:0), on)
#define sock_select(d, flags, onoff) do { \
(d)->event_mask = (onoff) ? \
((d)->event_mask | (flags)) : \
((d)->event_mask & ~(flags)); \
DEBUGF("sock_select(%ld): flags=%02X, onoff=%d, event_mask=%02lX", \
(long) (d)->port, (flags), (onoff), (unsigned long) (d)->event_mask); \
inet_driver_select((d)->port, (ErlDrvEvent)(long)(d)->event, (flags), (onoff)); \
} while(0)
#include <fcntl.h>
#ifdef NB_O_NDELAY /* Nothing needs this? */
# define NB_FLAG O_NDELAY
# ifndef ERRNO_BLOCK /* allow override (e.g. EAGAIN) via Makefile */
# define ERRNO_BLOCK EWOULDBLOCK
# endif
#else /* !NB_O_NDELAY */ /* The True Way - POSIX!:-) */
# define NB_FLAG O_NONBLOCK
# define ERRNO_BLOCK EAGAIN
#endif /* !NB_O_NDELAY */
#define SET_BLOCKING(fd) \
fcntl((fd), F_SETFL, fcntl((fd), F_GETFL, 0) & ~NB_FLAG)
#define SET_NONBLOCKING(fd) \
fcntl((fd), F_SETFL, fcntl((fd), F_GETFL, 0) | NB_FLAG)
#define HAVE_SOCKLEN_T
#ifdef HAVE_SOCKLEN_T
# define SOCKLEN_T socklen_t
#else
# define SOCKLEN_T int
#endif
#include "packet_parser.h"
#define get_int64(s) (((Uint64)(((unsigned char*) (s))[0]) << 56) | \
(((Uint64)((unsigned char*) (s))[1]) << 48) | \
(((Uint64)((unsigned char*) (s))[2]) << 40) | \
(((Uint64)((unsigned char*) (s))[3]) << 32) | \
(((Uint64)((unsigned char*) (s))[4]) << 24) | \
(((Uint64)((unsigned char*) (s))[5]) << 16) | \
(((Uint64)((unsigned char*) (s))[6]) << 8) | \
(((Uint64)((unsigned char*) (s))[7])))
#define put_int64(i, s) do {((char*)(s))[0] = (char)((int64_t)(i)>>56) & 0xff;\
((char*)(s))[1] = (char)((int64_t)(i)>>48) & 0xff;\
((char*)(s))[2] = (char)((int64_t)(i)>>40) & 0xff;\
((char*)(s))[3] = (char)((int64_t)(i)>>32) & 0xff;\
((char*)(s))[4] = (char)((int64_t)(i)>>24) & 0xff;\
((char*)(s))[5] = (char)((int64_t)(i)>>16) & 0xff;\
((char*)(s))[6] = (char)((int64_t)(i)>>8) & 0xff;\
((char*)(s))[7] = (char)((int64_t)(i)) & 0xff;\
} while (0)
#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \
(((unsigned char*) (s))[1] << 16) | \
(((unsigned char*) (s))[2] << 8) | \
(((unsigned char*) (s))[3]))
#define put_int32(i, s) do {((char*)(s))[0] = (char)((i) >> 24) & 0xff; \
((char*)(s))[1] = (char)((i) >> 16) & 0xff; \
((char*)(s))[2] = (char)((i) >> 8) & 0xff; \
((char*)(s))[3] = (char)(i) & 0xff;} \
while (0)
#define get_int24(s) ((((unsigned char*) (s))[0] << 16) | \
(((unsigned char*) (s))[1] << 8) | \
(((unsigned char*) (s))[2]))
#define put_int24(i, s) do {((char*)(s))[0] = (char)((i) >> 16) & 0xff; \
((char*)(s))[1] = (char)((i) >> 8) & 0xff; \
((char*)(s))[2] = (char)(i) & 0xff;} \
while (0)
#define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \
(((unsigned char*) (s))[1]))
#define put_int16(i, s) do {((char*)(s))[0] = (char)((i) >> 8) & 0xff; \
((char*)(s))[1] = (char)(i) & 0xff;} \
while (0)
#define get_int8(s) ((((unsigned char*) (s))[0] ))
#define put_int8(i, s) do {((unsigned char*)(s))[0] = (i) & 0xff;} while (0)
#define memzero(ptr, sz) memset((void*)(ptr),'\0',(sz))
#define VALGRIND_MAKE_MEM_DEFINED(ptr,size)
/*
Magic errno value used locally for return of {error, system_limit}
- the emulator definition of SYSTEM_LIMIT is not available here.
*/
#define INET_ERRNO_SYSTEM_LIMIT (15 << 8)
// Hack to handle R15 driver used with pre R15 driver
#if ERL_DRV_EXTENDED_MAJOR_VERSION == 1
typedef int ErlDrvSizeT;
typedef int ErlDrvSSizeT;
#endif
#if (ERL_DRV_EXTENDED_MAJOR_VERSION > 2) || ((ERL_DRV_EXTENDED_MAJOR_VERSION == 2) && (ERL_DRV_EXTENDED_MINOR_VERSION >= 1))
#define OUTPUT_TERM(p, message, len) erl_drv_output_term((p)->dport,(message),(len))
#define SEND_TERM(p, to, message, len) erl_drv_send_term((p)->dport,(to),(message),(len))
#else
#define OUTPUT_TERM(p, message, len) driver_output_term((p)->port,(message),(len))
#define SEND_TERM(p, to, message, len) driver_send_term((p)->port,(to),(message),(len))
#endif
/*----------------------------------------------------------------------------
** Interface constants.
**
** This section must be "identical" to the corresponding inet_int.hrl
*/
/* general address encode/decode tag */
#define INET_AF_INET 1
#define INET_AF_INET6 2
#define INET_AF_ANY 3 /* INADDR_ANY or IN6ADDR_ANY_INIT */
#define INET_AF_LOOPBACK 4 /* INADDR_LOOPBACK or IN6ADDR_LOOPBACK_INIT */
#define INET_AF_UNIX 5 /* maybe compatible ? */
/* open and INET_REQ_GETTYPE enumeration */
#define INET_TYPE_STREAM 1
#define INET_TYPE_DGRAM 2
#define INET_TYPE_SEQPACKET 3
/* INET_LOPT_MODE options */
#define INET_MODE_LIST 0
#define INET_MODE_BINARY 1
/* INET_LOPT_DELIVER options */
#define INET_DELIVER_PORT 0
#define INET_DELIVER_TERM 1
/* INET_LOPT_ACTIVE options */
#define INET_PASSIVE 0 /* false */
#define INET_ACTIVE 1 /* true */
#define INET_ONCE 2 /* true; active once then passive */
#define INET_MULTI 3 /* true; active N then passive */
/* INET_REQ_GETSTATUS enumeration */
#define INET_F_OPEN 0x0001
#define INET_F_BOUND 0x0002
#define INET_F_ACTIVE 0x0004
#define INET_F_LISTEN 0x0008
#define INET_F_CON 0x0010
#define INET_F_ACC 0x0020
#define INET_F_LST 0x0040
#define INET_F_BUSY 0x0080
#define INET_F_MULTI_CLIENT 0x0100 /* Multiple clients for one descriptor, i.e. multi-accept */
/* One numberspace for *_REC_* so if an e.g UDP request is issued
** for a TCP socket, the driver can protest.
*/
#define INET_REQ_OPEN 1
#define INET_REQ_CLOSE 2
#define INET_REQ_CONNECT 3
#define INET_REQ_PEER 4
#define INET_REQ_NAME 5
#define INET_REQ_BIND 6
#define INET_REQ_SETOPTS 7
#define INET_REQ_GETOPTS 8
/* #define INET_REQ_GETIX 9 NOT USED ANY MORE */
/* #define INET_REQ_GETIF 10 REPLACE BY NEW STUFF */
#define INET_REQ_GETSTAT 11
#define INET_REQ_GETHOSTNAME 12
#define INET_REQ_FDOPEN 13
#define INET_REQ_GETFD 14
#define INET_REQ_GETTYPE 15
#define INET_REQ_GETSTATUS 16
#define INET_REQ_GETSERVBYNAME 17
#define INET_REQ_GETSERVBYPORT 18
#define INET_REQ_SETNAME 19
#define INET_REQ_SETPEER 20
#define INET_REQ_GETIFLIST 21
#define INET_REQ_IFGET 22
#define INET_REQ_IFSET 23
#define INET_REQ_SUBSCRIBE 24
#define INET_REQ_GETIFADDRS 25
#define INET_REQ_ACCEPT 26
#define INET_REQ_LISTEN 27
#define INET_REQ_IGNOREFD 28
/* TCP requests */
/* #define TCP_REQ_ACCEPT 40 MOVED */
/* #define TCP_REQ_LISTEN 41 MERGED */
#define TCP_REQ_RECV 42
#define TCP_REQ_UNRECV 43
#define TCP_REQ_SHUTDOWN 44
/* UDP and SCTP requests */
/* INET_REQ_SUBSCRIBE sub-requests */
#define INET_SUBS_EMPTY_OUT_Q 1
/* TCP additional flags */
#define TCP_ADDF_DELAY_SEND 1
#define TCP_ADDF_CLOSE_SENT 2 /* Close sent (active mode only) */
#define TCP_ADDF_DELAYED_CLOSE_RECV 4 /* If receive fails, report {error,closed} (passive mode) */
#define TCP_ADDF_DELAYED_CLOSE_SEND 8 /* If send fails, report {error,closed} (passive mode) */
/* *_REQ_* replies */
#define INET_REP_ERROR 0
#define INET_REP_OK 1
// #define INET_REP 2 - only used by SCTP in inet_drv
/* INET_REQ_SETOPTS and INET_REQ_GETOPTS options */
#define INET_OPT_REUSEADDR 0 /* enable/disable local address reuse */
#define INET_OPT_KEEPALIVE 1 /* enable/disable keep connections alive */
#define INET_OPT_DONTROUTE 2 /* enable/disable routing for messages */
#define INET_OPT_LINGER 3 /* linger on close if data is present */
#define INET_OPT_BROADCAST 4 /* enable/disable transmission of broadcast */
#define INET_OPT_OOBINLINE 5 /* enable/disable out-of-band data in band */
#define INET_OPT_SNDBUF 6 /* set send buffer size */
#define INET_OPT_RCVBUF 7 /* set receive buffer size */
#define INET_OPT_PRIORITY 8 /* set priority */
#define INET_OPT_TOS 9 /* Set type of service */
#define TCP_OPT_NODELAY 10 /* don't delay send to coalesce packets */
#define UDP_OPT_MULTICAST_IF 11 /* set/get IP multicast interface */
#define UDP_OPT_MULTICAST_TTL 12 /* set/get IP multicast timetolive */
#define UDP_OPT_MULTICAST_LOOP 13 /* set/get IP multicast loopback */
#define UDP_OPT_ADD_MEMBERSHIP 14 /* add an IP group membership */
#define UDP_OPT_DROP_MEMBERSHIP 15 /* drop an IP group membership */
#define INET_OPT_IPV6_V6ONLY 16 /* IPv6 only socket, no mapped v4 addrs */
/* LOPT is local options */
#define INET_LOPT_BUFFER 20 /* min buffer size hint */
#define INET_LOPT_HEADER 21 /* list header size */
#define INET_LOPT_ACTIVE 22 /* enable/disable active receive */
#define INET_LOPT_PACKET 23 /* packet header type (TCP) */
#define INET_LOPT_MODE 24 /* list or binary mode */
#define INET_LOPT_DELIVER 25 /* port or term delivery */
#define INET_LOPT_EXITONCLOSE 26 /* exit port on active close or not ! */
#define INET_LOPT_TCP_HIWTRMRK 27 /* set local high watermark */
#define INET_LOPT_TCP_LOWTRMRK 28 /* set local low watermark */
/* 29 unused */
#define INET_LOPT_TCP_SEND_TIMEOUT 30 /* set send timeout */
#define INET_LOPT_TCP_DELAY_SEND 31 /* Delay sends until next poll */
#define INET_LOPT_PACKET_SIZE 32 /* Max packet size */
#define INET_LOPT_UDP_READ_PACKETS 33 /* Number of packets to read */
#define INET_OPT_RAW 34 /* Raw socket options */
#define INET_LOPT_TCP_SEND_TIMEOUT_CLOSE 35 /* auto-close on send timeout or not */
#define INET_LOPT_TCP_MSGQ_HIWTRMRK 36 /* set local high watermark */
#define INET_LOPT_TCP_MSGQ_LOWTRMRK 37 /* set local low watermark */
#define INET_LOPT_NETNS 38 /* Network namespace pathname */
#define INET_LOPT_TCP_SHOW_ECONNRESET 39 /* tell user about incoming RST */
#define INET_LOPT_LINE_DELIM 40 /* Line delimiting char */
#define UNIX_OPT_PEERCRED 201
#define UNIX_OPT_PEERPID 202
/* INET_REQ_GETSTAT enumeration */
#define INET_STAT_RECV_CNT 1
#define INET_STAT_RECV_MAX 2
#define INET_STAT_RECV_AVG 3
#define INET_STAT_RECV_DVI 4
#define INET_STAT_SEND_CNT 5
#define INET_STAT_SEND_MAX 6
#define INET_STAT_SEND_AVG 7
#define INET_STAT_SEND_PND 8
#define INET_STAT_RECV_OCT 9 /* received octets */
#define INET_STAT_SEND_OCT 10 /* sent octets */
/*
** End of interface constants.
**--------------------------------------------------------------------------*/
#define INET_STATE_CLOSED (0)
#define INET_STATE_OPEN (INET_F_OPEN)
#define INET_STATE_BOUND (INET_STATE_OPEN | INET_F_BOUND)
#define INET_STATE_CONNECTED (INET_STATE_BOUND | INET_F_ACTIVE)
#define INET_STATE_LISTENING (INET_STATE_BOUND | INET_F_LISTEN)
#define INET_STATE_CONNECTING (INET_STATE_BOUND | INET_F_CON)
#define INET_STATE_ACCEPTING (INET_STATE_LISTENING | INET_F_ACC)
#define INET_STATE_MULTI_ACCEPTING (INET_STATE_ACCEPTING | INET_F_MULTI_CLIENT)
#define IS_OPEN(d) \
(((d)->state & INET_F_OPEN) == INET_F_OPEN)
#define IS_BOUND(d) \
(((d)->state & INET_F_BOUND) == INET_F_BOUND)
#define IS_CONNECTED(d) \
(((d)->state & INET_STATE_CONNECTED) == INET_STATE_CONNECTED)
#define IS_CONNECTING(d) \
(((d)->state & INET_F_CON) == INET_F_CON)
#define IS_BUSY(d) \
(((d)->state & INET_F_BUSY) == INET_F_BUSY)
#define INET_MAX_OPT_BUFFER (64*1024)
#define INET_DEF_BUFFER 1460 /* default buffer size */
#define INET_MIN_BUFFER 1 /* internal min buffer */
#define INET_HIGH_WATERMARK (1024*8) /* 8k pending high => busy */
#define INET_LOW_WATERMARK (1024*4) /* 4k pending => allow more */
#define INET_HIGH_MSGQ_WATERMARK (1024*8) /* 8k pending high => busy */
#define INET_LOW_MSGQ_WATERMARK (1024*4) /* 4k pending => allow more */
#define INET_INFINITY 0xffffffff /* infinity value */
#define INET_MAX_ASYNC 1 /* max number of async queue ops */
/* INET_LOPT_UDP_PACKETS */
#define INET_PACKET_POLL 5 /* maximum number of packets to poll */
/* INET Ignore states */
#define INET_IGNORE_NONE 0
#define INET_IGNORE_READ 1
#define INET_IGNORE_WRITE 1 << 1
/* Max length of Erlang Term Buffer (for outputting structured terms): */
#define PACKET_ERL_DRV_TERM_DATA_LEN 32
#define BIN_REALLOC_MARGIN(x) ((x)/4) /* 25% */
/* The general purpose sockaddr */
typedef union {
struct sockaddr sa;
struct sockaddr_in sai;
struct sockaddr_un sau;
#ifdef HAVE_IN6
struct sockaddr_in6 sai6;
#endif
} inet_address;
/* for AF_INET & AF_INET6 */
#define inet_address_port(x) ((x)->sai.sin_port)
#if defined(HAVE_IN6) && defined(AF_INET6)
#define addrlen(family) \
((family == AF_INET) ? sizeof(struct in_addr) : \
((family == AF_INET6) ? sizeof(struct in6_addr) : 0))
#else
#define addrlen(family) \
((family == AF_INET) ? sizeof(struct in_addr) : 0)
#endif
typedef struct _multi_timer_data {
ErlDrvNowData when;
ErlDrvTermData caller;
void (*timeout_function)(ErlDrvData drv_data, ErlDrvTermData caller);
struct _multi_timer_data *next;
struct _multi_timer_data *prev;
} MultiTimerData;
static MultiTimerData *add_multi_timer(MultiTimerData **first, ErlDrvPort port,
ErlDrvTermData caller, unsigned timeout,
void (*timeout_fun)(ErlDrvData drv_data,
ErlDrvTermData caller));
static void fire_multi_timers(MultiTimerData **first, ErlDrvPort port,
ErlDrvData data);
static void remove_multi_timer(MultiTimerData **first, ErlDrvPort port, MultiTimerData *p);
static void afunix_multi_timeout(ErlDrvData e, ErlDrvTermData caller);
static void clean_multi_timers(MultiTimerData **first, ErlDrvPort port);
typedef struct {
int id; /* id used to identify reply */
ErlDrvTermData caller; /* recipient of async reply */
int req; /* Request id (CONNECT/ACCEPT/RECV) */
union {
unsigned value; /* Request timeout (since op issued,not started) */
MultiTimerData *mtd;
} tmo;
ErlDrvMonitor monitor;
} inet_async_op;
typedef struct inet_async_multi_op_ {
inet_async_op op;
struct inet_async_multi_op_ *next;
} inet_async_multi_op;
typedef struct subs_list_ {
ErlDrvTermData subscriber;
struct subs_list_ *next;
} subs_list;
#define NO_PROCESS 0
#define NO_SUBSCRIBERS(SLP) ((SLP)->subscriber == NO_PROCESS)
typedef struct {
SOCKET s; /* the socket or INVALID_SOCKET if not open */
HANDLE event; /* Event handle (same as s in unix) */
long event_mask; /* current FD events */
ErlDrvPort port; /* the port identifier */
ErlDrvTermData dport; /* the port identifier as DriverTermData */
int state; /* status */
int prebound; /* only set when opened with inet_fdopen */
int mode; /* BINARY | LIST
(affect how to interpret hsz) */
int exitf; /* exit port on close or not */
int deliver; /* Delivery mode, TERM or PORT */
ErlDrvTermData caller; /* recipient of sync reply */
ErlDrvTermData busy_caller; /* recipient of sync reply when caller busy.
* Only valid while INET_F_BUSY. */
inet_async_op* oph; /* queue head or NULL */
inet_async_op* opt; /* queue tail or NULL */
inet_async_op op_queue[INET_MAX_ASYNC]; /* call queue */
int active; /* 0 = passive, 1 = active, 2 = active once */
int16_t active_count; /* counter for {active,N} */
int stype; /* socket type:
SOCK_STREAM/SOCK_DGRAM/SOCK_SEQPACKET */
int sprotocol; /* socket protocol:
IPPROTO_TCP|IPPROTO_UDP|IPPROTO_SCTP */
int sfamily; /* address family */
enum PacketParseType htype; /* header type (TCP only?) */
unsigned int psize; /* max packet size (TCP only?) */
inet_address remote; /* remote address for connected sockets */
inet_address peer_addr; /* fake peer address */
inet_address name_addr; /* fake local address */
inet_address* peer_ptr; /* fake peername or NULL */
inet_address* name_ptr; /* fake sockname or NULL */
int bufsz; /* minimum buffer constraint */
unsigned int hsz; /* the list header size, -1 is large !!! */
/* statistics */
uint64_t recv_oct; /* number of received octets, 64 bits */
unsigned long recv_cnt; /* number of packets received */
unsigned long recv_max; /* maximum packet size received */
double recv_avg; /* average packet size received */
double recv_dvi; /* avarage deviation from avg_size */
uint64_t send_oct; /* number of octets sent, 64 bits */
char delimiter; /* Line delimiting character (def: '\n') */
unsigned long send_cnt; /* number of packets sent */
unsigned long send_max; /* maximum packet send */
double send_avg; /* average packet size sent */
subs_list empty_out_q_subs; /* Empty out queue subscribers */
int is_ignored; /* if a fd is ignored by the inet_drv.
This flag should be set to true when
the fd is used outside of inet_drv. */
} inet_descriptor;
static void send_to_subscribers(inet_descriptor* desc, subs_list *, int,
ErlDrvTermData [], int);
static void free_subscribers(subs_list*);
static int save_subscriber(subs_list *, ErlDrvTermData);
#define TCP_MAX_PACKET_SIZE 0x4000000 /* 64 M */
#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O
* vector sock_sendv().
*/
static int afunix_init(void);
static void afunix_finish(void);
static void afunix_stop(ErlDrvData);
static void afunix_command(ErlDrvData, char*, ErlDrvSizeT);
static void afunix_commandv(ErlDrvData, ErlIOVec*);
static void afunix_flush(ErlDrvData drv_data);
static void afunix_drv_input(ErlDrvData, ErlDrvEvent);
static void afunix_drv_output(ErlDrvData data, ErlDrvEvent event);
static ErlDrvData afunix_start(ErlDrvPort, char* command);
static ErlDrvSSizeT afunix_ctl(ErlDrvData, unsigned int,
char*, ErlDrvSizeT, char**, ErlDrvSizeT);
static void afunix_timeout(ErlDrvData);
static void afunix_process_exit(ErlDrvData, ErlDrvMonitor *);
static void inet_stop_select(ErlDrvEvent, void*);
static ErlDrvEntry afunix_driver_entry;
typedef struct {
inet_descriptor inet; /* common data structure (DON'T MOVE) */
int high; /* high watermark */
int low; /* low watermark */
int send_timeout; /* timeout to use in send */
int send_timeout_close; /* auto-close socket on send_timeout */
int busy_on_send; /* busy on send with timeout! */
int i_bufsz; /* current input buffer size (<= bufsz) */
ErlDrvBinary* i_buf; /* current binary buffer */
char* i_ptr; /* current pos in buf */
char* i_ptr_start; /* packet start pos in buf */
int i_remain; /* remaining chars to read */
int tcp_add_flags;/* Additional TCP descriptor flags */
int http_state; /* 0 = response|request 1=headers fields */
inet_async_multi_op *multi_first;/* NULL == no multi-accept-queue, op is in ordinary queue */
inet_async_multi_op *multi_last;
MultiTimerData *mtd; /* Timer structures for multiple accept */
} tcp_descriptor;
/* send function */
static int tcp_send(tcp_descriptor* desc, char* ptr, ErlDrvSizeT len);
static int tcp_sendv(tcp_descriptor* desc, ErlIOVec* ev);
static int tcp_recv(tcp_descriptor* desc, int request_len);
static int tcp_deliver(tcp_descriptor* desc, int len);
static int afunix_output(tcp_descriptor* desc, HANDLE event);
static int afunix_input(tcp_descriptor* desc, HANDLE event);
/* convert descriptor poiner to inet_descriptor pointer */
#define INETP(d) (&(d)->inet)
static int async_ref = 0; /* async reference id generator */
#define NEW_ASYNC_ID() ((async_ref++) & 0xffff)
/* check for transition from active to passive */
#define INET_CHECK_ACTIVE_TO_PASSIVE(inet) \
do { \
if ((inet)->active == INET_ONCE) \
(inet)->active = INET_PASSIVE; \
else if ((inet)->active == INET_MULTI && --((inet)->active_count) == 0) { \
(inet)->active = INET_PASSIVE; \
packet_passive_message(inet); \
} \
} while (0)
static ErlDrvTermData am_ok;
static ErlDrvTermData am_tcp;
static ErlDrvTermData am_udp;
static ErlDrvTermData am_error;
static ErlDrvTermData am_inet_async;
static ErlDrvTermData am_inet_reply;
static ErlDrvTermData am_timeout;
static ErlDrvTermData am_closed;
static ErlDrvTermData am_tcp_passive;
static ErlDrvTermData am_tcp_closed;
static ErlDrvTermData am_tcp_error;
static ErlDrvTermData am_udp_error;
static ErlDrvTermData am_empty_out_q;
static ErlDrvTermData am_ssl_tls;
/* speical errors for bad ports and sequences */
#define EXBADPORT "exbadport"
#define EXBADSEQ "exbadseq"
static int inet_init(void);
static ErlDrvSSizeT ctl_reply(int, char*, ErlDrvSizeT, char**, ErlDrvSizeT);
#if HAVE_IN6
# if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY
# if HAVE_DECL_IN6ADDR_ANY_INIT
static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } };
# else
static const struct in6_addr in6addr_any =
{ { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
# endif /* HAVE_IN6ADDR_ANY_INIT */
# endif /* ! HAVE_DECL_IN6ADDR_ANY */
# if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK
# if HAVE_DECL_IN6ADDR_LOOPBACK_INIT
static const struct in6_addr in6addr_loopback =
{ { IN6ADDR_LOOPBACK_INIT } };
# else
static const struct in6_addr in6addr_loopback =
{ { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } };
# endif /* HAVE_IN6ADDR_LOOPBACk_INIT */
# endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */
#endif /* HAVE_IN6 */
static void fatal_exit(int n, char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(n);
}
/*
* Malloc wrapper,
* we would like to change the behaviour for different
* systems here.
*/
#ifdef FATAL_MALLOC
static void *alloc_wrapper(ErlDrvSizeT size){
void *ret = driver_alloc(size);
if(ret == NULL)
fatal_exit(1,"Out of virtual memory in malloc (%s)", __FILE__);
return ret;
}
#define ALLOC(X) alloc_wrapper(X)
static void *realloc_wrapper(void *current, ErlDrvSizeT size){
void *ret = driver_realloc(current,size);
if(ret == NULL)
fatal_exit(1,"Out of virtual memory in realloc (%s)", __FILE__);
return ret;
}
#define REALLOC(X,Y) realloc_wrapper(X,Y)
#define FREE(P) driver_free((P))
#else /* FATAL_MALLOC */
#define ALLOC(X) driver_alloc((X))
#define REALLOC(X,Y) driver_realloc((X), (Y))
#define FREE(P) driver_free((P))
#endif /* FATAL_MALLOC */
#define INIT_ATOM(NAME) am_ ## NAME = driver_mk_atom(#NAME)
#define LOAD_ATOM_CNT 2
#define LOAD_ATOM(vec, i, atom) \
(((vec)[(i)] = ERL_DRV_ATOM), \
((vec)[(i)+1] = (atom)), \
((i)+LOAD_ATOM_CNT))
#define LOAD_INT_CNT 2
#define LOAD_INT(vec, i, val) \
(((vec)[(i)] = ERL_DRV_INT), \
((vec)[(i)+1] = (ErlDrvTermData)(val)), \
((i)+LOAD_INT_CNT))
#define LOAD_UINT_CNT 2
#define LOAD_UINT(vec, i, val) \
(((vec)[(i)] = ERL_DRV_UINT), \
((vec)[(i)+1] = (ErlDrvTermData)(val)), \
((i)+LOAD_UINT_CNT))
#define LOAD_PORT_CNT 2
#define LOAD_PORT(vec, i, port) \
(((vec)[(i)] = ERL_DRV_PORT), \
((vec)[(i)+1] = (port)), \
((i)+LOAD_PORT_CNT))
#define LOAD_PID_CNT 2
#define LOAD_PID(vec, i, pid) \
(((vec)[(i)] = ERL_DRV_PID), \
((vec)[(i)+1] = (pid)), \
((i)+LOAD_PID_CNT))
#define LOAD_BINARY_CNT 4
#define LOAD_BINARY(vec, i, bin, offs, len) \
(((vec)[(i)] = ERL_DRV_BINARY), \
((vec)[(i)+1] = (ErlDrvTermData)(bin)), \
((vec)[(i)+2] = (len)), \
((vec)[(i)+3] = (offs)), \
((i)+LOAD_BINARY_CNT))
#define LOAD_BUF2BINARY_CNT 3
#define LOAD_BUF2BINARY(vec, i, buf, len) \
(((vec)[(i)] = ERL_DRV_BUF2BINARY), \
((vec)[(i)+1] = (ErlDrvTermData)(buf)), \
((vec)[(i)+2] = (len)), \
((i)+LOAD_BUF2BINARY_CNT))
#define LOAD_STRING_CNT 3
#define LOAD_STRING(vec, i, str, len) \
(((vec)[(i)] = ERL_DRV_STRING), \
((vec)[(i)+1] = (ErlDrvTermData)(str)), \
((vec)[(i)+2] = (len)), \
((i)+LOAD_STRING_CNT))
#define LOAD_STRING_CONS_CNT 3
#define LOAD_STRING_CONS(vec, i, str, len) \
(((vec)[(i)] = ERL_DRV_STRING_CONS), \
((vec)[(i)+1] = (ErlDrvTermData)(str)), \
((vec)[(i)+2] = (len)), \
((i)+LOAD_STRING_CONS_CNT))
#define LOAD_TUPLE_CNT 2
#define LOAD_TUPLE(vec, i, size) \
(((vec)[(i)] = ERL_DRV_TUPLE), \
((vec)[(i)+1] = (size)), \
((i)+LOAD_TUPLE_CNT))
#define LOAD_NIL_CNT 1
#define LOAD_NIL(vec, i) \
(((vec)[(i)] = ERL_DRV_NIL), \
((i)+LOAD_NIL_CNT))
#define LOAD_LIST_CNT 2
#define LOAD_LIST(vec, i, size) \
(((vec)[(i)] = ERL_DRV_LIST), \
((vec)[(i)+1] = (size)), \
((i)+LOAD_LIST_CNT))
/* Assume a cache line size of 64 bytes */
#define INET_DRV_CACHE_LINE_SIZE ((ErlDrvUInt) 64)
#define INET_DRV_CACHE_LINE_MASK (INET_DRV_CACHE_LINE_SIZE - 1)
static int debug_level = DLOG_DEFAULT;
static void emit_log(int level, char* file, int line, ...)
{
va_list ap;
char* fmt;
if ((level == DLOG_EMERGENCY) ||
((debug_level >= 0) && (level <= debug_level))) {
va_start(ap, line);
fmt = va_arg(ap, char*);
fprintf(stderr, "%s:%d: ", file, line);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\r\n");
va_end(ap);
}
}
/*
** Binary Buffer Managment
** We keep a stack of usable buffers
*/
#define BUFFER_STACK_SIZE 14
#define BUFFER_STACK_MAX_MEM_SIZE (1024*1024)
ErlDrvTSDKey buffer_stack_key;
typedef struct {
int mem_size;
int pos;
ErlDrvBinary* stk[BUFFER_STACK_SIZE];
} InetDrvBufStkBase;
typedef struct {
InetDrvBufStkBase buf;
char align[(((sizeof(InetDrvBufStkBase) - 1) / INET_DRV_CACHE_LINE_SIZE) + 1)
* INET_DRV_CACHE_LINE_SIZE];
} InetDrvBufStk;
static InetDrvBufStk *get_bufstk(void)
{
InetDrvBufStk *bs = erl_drv_tsd_get(buffer_stack_key);
if (bs)
return bs;
bs = driver_alloc(sizeof(InetDrvBufStk)
+ INET_DRV_CACHE_LINE_SIZE - 1);
if (!bs)
return NULL;
if ((((ErlDrvUInt) bs) & INET_DRV_CACHE_LINE_MASK) != 0)
bs = ((InetDrvBufStk *)
((((ErlDrvUInt) bs) & ~INET_DRV_CACHE_LINE_MASK)
+ INET_DRV_CACHE_LINE_SIZE));
erl_drv_tsd_set(buffer_stack_key, bs);
bs->buf.pos = 0;
bs->buf.mem_size = 0;
return bs;
}
static ErlDrvBinary* alloc_buffer(ErlDrvSizeT minsz)
{
InetDrvBufStk *bs = get_bufstk();
DEBUGF("alloc_buffer: "LLU, (llu_t)minsz);
if (bs && bs->buf.pos > 0) {
long size;
ErlDrvBinary* buf = bs->buf.stk[--bs->buf.pos];
size = buf->orig_size;
bs->buf.mem_size -= size;
if (size >= minsz)
return buf;
driver_free_binary(buf);
}
return driver_alloc_binary(minsz);
}
static void release_buffer(ErlDrvBinary* buf)
{
InetDrvBufStk *bs;
long size;
DEBUGF("release_buffer: %ld", (buf==NULL) ? 0 : buf->orig_size);
if (!buf)
return;
size = buf->orig_size;
if (size > BUFFER_STACK_MAX_MEM_SIZE)
goto free_binary;
bs = get_bufstk();
if (!bs
|| (bs->buf.mem_size + size > BUFFER_STACK_MAX_MEM_SIZE)
|| (bs->buf.pos >= BUFFER_STACK_SIZE)) {
free_binary:
driver_free_binary(buf);
}
else {
bs->buf.mem_size += size;
bs->buf.stk[bs->buf.pos++] = buf;
}
}
/* use a TRICK, access the refc field to see if any one else has