-
Notifications
You must be signed in to change notification settings - Fork 19
/
sendmsg.c
147 lines (121 loc) · 3.57 KB
/
sendmsg.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
/* Copyright © 2012 Andre Nathan <[email protected]> */
/*
* These functions are adapted from Stevens, Fenner and Rudoff, UNIX Network
* Programming, Volume 1, Third Edition. We use CMSG_LEN instead of CMSG_SPACE
* for the msg_controllen field of struct msghdr to avoid breaking LP64
* systems (cf. Postfix source code).
*/
#define EXTUNIX_WANT_SENDMSG
#include "config.h"
#if defined(EXTUNIX_HAVE_SENDMSG)
CAMLprim value caml_extunix_sendmsg(value fd_val, value sendfd_val, value data_val)
{
CAMLparam3(fd_val, sendfd_val, data_val);
CAMLlocal1(data);
size_t datalen;
struct msghdr msg;
struct iovec iov[1];
int fd = Int_val(fd_val);
ssize_t ret;
char *buf;
#if defined(CMSG_SPACE)
union {
struct cmsghdr cmsg; /* for alignment */
char control[CMSG_SPACE(sizeof(int))]; /* sizeof sendfd */
} control_un;
#endif
memset(&msg, 0, sizeof msg);
if (sendfd_val != Val_none)
{
int sendfd = Int_val(Some_val(sendfd_val));
#if defined(CMSG_SPACE)
struct cmsghdr *cmsgp;
msg.msg_control = control_un.control;
msg.msg_controllen = CMSG_LEN(sizeof sendfd);
cmsgp = CMSG_FIRSTHDR(&msg);
cmsgp->cmsg_len = CMSG_LEN(sizeof sendfd);
cmsgp->cmsg_level = SOL_SOCKET;
cmsgp->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsgp) = sendfd;
#else
msg.msg_accrights = (caddr_t)&sendfd;
msg.msg_accrightslen = sizeof sendfd;
#endif
}
datalen = caml_string_length(data_val);
buf = caml_stat_alloc(datalen);
memcpy(buf, String_val(data_val), datalen);
iov[0].iov_base = buf;
iov[0].iov_len = datalen;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
caml_enter_blocking_section();
ret = sendmsg(fd, &msg, 0);
caml_leave_blocking_section();
caml_stat_free(buf);
if (ret == -1)
uerror("sendmsg", Nothing);
CAMLreturn (Val_unit);
}
CAMLprim value caml_extunix_recvmsg(value fd_val)
{
CAMLparam1(fd_val);
CAMLlocal2(data, res);
struct msghdr msg;
int fd = Int_val(fd_val);
int recvfd;
ssize_t len;
struct iovec iov[1];
char buf[4096];
#if defined(CMSG_SPACE)
union {
struct cmsghdr cmsg; /* just for alignment */
char control[CMSG_SPACE(sizeof recvfd)];
} control_un;
struct cmsghdr *cmsgp;
memset(&msg, 0, sizeof msg);
msg.msg_control = control_un.control;
msg.msg_controllen = CMSG_LEN(sizeof recvfd);
#else
msg.msg_accrights = (caddr_t)&recvfd;
msg.msg_accrightslen = sizeof recvfd;
#endif
iov[0].iov_base = buf;
iov[0].iov_len = sizeof buf;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
caml_enter_blocking_section();
len = recvmsg(fd, &msg, 0);
caml_leave_blocking_section();
if (len == -1)
uerror("recvmsg", Nothing);
res = caml_alloc(2, 0);
#if defined(CMSG_SPACE)
cmsgp = CMSG_FIRSTHDR(&msg);
if (cmsgp == NULL) {
Store_field(res, 0, Val_none);
} else {
CAMLlocal1(some_fd);
if (cmsgp->cmsg_len != CMSG_LEN(sizeof recvfd))
unix_error(EINVAL, "recvmsg", caml_copy_string("wrong descriptor size"));
if (cmsgp->cmsg_level != SOL_SOCKET || cmsgp->cmsg_type != SCM_RIGHTS)
unix_error(EINVAL, "recvmsg", caml_copy_string("invalid protocol"));
some_fd = caml_alloc(1, 0);
Store_field(some_fd, 0, Val_int(*(int *)CMSG_DATA(cmsgp)));
Store_field(res, 0, some_fd);
}
#else
if (msg.msg_accrightslen != sizeof recvfd) {
Store_field(res, 0, Val_none);
} else {
CAMLlocal1(some_fd);
some_fd = caml_alloc(1, 0);
Store_field(some_fd, 0, Val_int(recvfd));
Store_field(res, 0, some_fd);
}
#endif
data = caml_alloc_initialized_string(len, buf);
Store_field(res, 1, data);
CAMLreturn (res);
}
#endif /* EXTUNIX_HAVE_SENDMSG */