/src/samba/lib/util/msghdr.c
Line | Count | Source |
1 | | /* |
2 | | * Unix SMB/CIFS implementation. |
3 | | * Copyright (C) Volker Lendecke 2014 |
4 | | * |
5 | | * This program is free software; you can redistribute it and/or modify |
6 | | * it under the terms of the GNU General Public License as published by |
7 | | * the Free Software Foundation; either version 3 of the License, or |
8 | | * (at your option) any later version. |
9 | | * |
10 | | * This program is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | * GNU General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU General Public License |
16 | | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | #include "replace.h" |
20 | | #include "lib/util/msghdr.h" |
21 | | #include "lib/util/iov_buf.h" |
22 | | #include <sys/socket.h> |
23 | | |
24 | | #if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) |
25 | | |
26 | | ssize_t msghdr_prep_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize, |
27 | | const int *fds, size_t num_fds) |
28 | 0 | { |
29 | 0 | size_t fds_size = sizeof(int) * MIN(num_fds, INT8_MAX); |
30 | 0 | size_t cmsg_len = CMSG_LEN(fds_size); |
31 | 0 | size_t cmsg_space = CMSG_SPACE(fds_size); |
32 | 0 | struct cmsghdr *cmsg; |
33 | 0 | void *fdptr; |
34 | |
|
35 | 0 | if (num_fds == 0) { |
36 | 0 | if (msg != NULL) { |
37 | 0 | msg->msg_control = NULL; |
38 | 0 | msg->msg_controllen = 0; |
39 | 0 | } |
40 | | /* |
41 | | * C99 doesn't allow 0-length arrays |
42 | | */ |
43 | 0 | return 1; |
44 | 0 | } |
45 | 0 | if (num_fds > INT8_MAX) { |
46 | 0 | return -1; |
47 | 0 | } |
48 | 0 | if ((msg == NULL) || (cmsg_space > bufsize)) { |
49 | | /* |
50 | | * C99 doesn't allow 0-length arrays |
51 | | */ |
52 | 0 | return MAX(cmsg_space, 1); |
53 | 0 | } |
54 | | |
55 | 0 | msg->msg_control = buf; |
56 | 0 | msg->msg_controllen = cmsg_space; |
57 | |
|
58 | 0 | cmsg = CMSG_FIRSTHDR(msg); |
59 | 0 | cmsg->cmsg_level = SOL_SOCKET; |
60 | 0 | cmsg->cmsg_type = SCM_RIGHTS; |
61 | 0 | cmsg->cmsg_len = cmsg_len; |
62 | 0 | fdptr = CMSG_DATA(cmsg); |
63 | 0 | memcpy(fdptr, fds, fds_size); |
64 | 0 | msg->msg_controllen = cmsg->cmsg_len; |
65 | |
|
66 | 0 | return cmsg_space; |
67 | 0 | } |
68 | | |
69 | | size_t msghdr_prep_recv_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize, |
70 | | size_t num_fds) |
71 | 0 | { |
72 | 0 | size_t ret = CMSG_SPACE(sizeof(int) * num_fds); |
73 | |
|
74 | 0 | if (bufsize < ret) { |
75 | 0 | return ret; |
76 | 0 | } |
77 | 0 | if (msg != NULL) { |
78 | 0 | if (num_fds != 0) { |
79 | 0 | msg->msg_control = buf; |
80 | 0 | msg->msg_controllen = ret; |
81 | 0 | } else { |
82 | 0 | msg->msg_control = NULL; |
83 | 0 | msg->msg_controllen = 0; |
84 | 0 | } |
85 | 0 | } |
86 | 0 | return ret; |
87 | 0 | } |
88 | | |
89 | | size_t msghdr_extract_fds(struct msghdr *msg, int *fds, size_t fds_size) |
90 | 0 | { |
91 | 0 | struct cmsghdr *cmsg; |
92 | 0 | size_t num_fds; |
93 | |
|
94 | 0 | for(cmsg = CMSG_FIRSTHDR(msg); |
95 | 0 | cmsg != NULL; |
96 | 0 | cmsg = CMSG_NXTHDR(msg, cmsg)) |
97 | 0 | { |
98 | 0 | if ((cmsg->cmsg_type == SCM_RIGHTS) && |
99 | 0 | (cmsg->cmsg_level == SOL_SOCKET)) { |
100 | 0 | break; |
101 | 0 | } |
102 | 0 | } |
103 | |
|
104 | 0 | if (cmsg == NULL) { |
105 | 0 | return 0; |
106 | 0 | } |
107 | | |
108 | 0 | num_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); |
109 | |
|
110 | 0 | if ((num_fds != 0) && (fds != NULL) && (fds_size >= num_fds)) { |
111 | 0 | memcpy(fds, CMSG_DATA(cmsg), num_fds * sizeof(int)); |
112 | 0 | } |
113 | |
|
114 | 0 | return num_fds; |
115 | 0 | } |
116 | | |
117 | | #elif defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS) |
118 | | |
119 | | ssize_t msghdr_prep_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize, |
120 | | const int *fds, size_t num_fds) |
121 | | { |
122 | | size_t needed; |
123 | | |
124 | | if (num_fds > INT8_MAX) { |
125 | | return -1; |
126 | | } |
127 | | |
128 | | needed = sizeof(int) * num_fds; |
129 | | |
130 | | if ((msg == NULL) || (needed > bufsize)) { |
131 | | return needed; |
132 | | } |
133 | | |
134 | | memcpy(buf, fds, needed); |
135 | | |
136 | | msg->msg_accrights = (caddr_t) buf; |
137 | | msg->msg_accrightslen = needed; |
138 | | |
139 | | return needed; |
140 | | } |
141 | | |
142 | | size_t msghdr_prep_recv_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize, |
143 | | size_t num_fds) |
144 | | { |
145 | | size_t ret = num_fds * sizeof(int); |
146 | | |
147 | | if (bufsize < ret) { |
148 | | return ret; |
149 | | } |
150 | | |
151 | | if (msg != NULL) { |
152 | | if (num_fds != 0) { |
153 | | msg->msg_accrights = (caddr_t) buf; |
154 | | msg->msg_accrightslen = ret; |
155 | | } else { |
156 | | msg->msg_accrights = NULL; |
157 | | msg->msg_accrightslen = 0; |
158 | | } |
159 | | } |
160 | | return ret; |
161 | | } |
162 | | |
163 | | size_t msghdr_extract_fds(struct msghdr *msg, int *fds, size_t fds_size) |
164 | | { |
165 | | size_t num_fds = msg->msg_accrightslen / sizeof(int); |
166 | | |
167 | | if ((fds != 0) && (num_fds <= fds_size)) { |
168 | | memcpy(fds, msg->msg_accrights, msg->msg_accrightslen); |
169 | | } |
170 | | |
171 | | return num_fds; |
172 | | } |
173 | | |
174 | | #else |
175 | | |
176 | | ssize_t msghdr_prep_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize, |
177 | | const int *fds, size_t num_fds) |
178 | | { |
179 | | return -1; |
180 | | } |
181 | | |
182 | | size_t msghdr_prep_recv_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize, |
183 | | size_t num_fds) |
184 | | { |
185 | | return 0; |
186 | | } |
187 | | |
188 | | size_t msghdr_extract_fds(struct msghdr *msg, int *fds, size_t fds_size) |
189 | | { |
190 | | return 0; |
191 | | } |
192 | | |
193 | | #endif |
194 | | |
195 | | struct msghdr_buf { |
196 | | struct msghdr msg; |
197 | | struct sockaddr_storage addr; |
198 | | struct iovec iov; |
199 | | uint8_t buf[]; |
200 | | }; |
201 | | |
202 | | ssize_t msghdr_copy(struct msghdr_buf *msg, size_t msgsize, |
203 | | const void *addr, socklen_t addrlen, |
204 | | const struct iovec *iov, int iovcnt, |
205 | | const int *fds, size_t num_fds) |
206 | 0 | { |
207 | 0 | ssize_t fd_len; |
208 | 0 | size_t iov_len, needed, bufsize; |
209 | |
|
210 | 0 | bufsize = (msgsize > offsetof(struct msghdr_buf, buf)) ? |
211 | 0 | msgsize - offsetof(struct msghdr_buf, buf) : 0; |
212 | |
|
213 | 0 | if (msg != NULL) { |
214 | 0 | msg->msg = (struct msghdr) { 0 }; |
215 | |
|
216 | 0 | fd_len = msghdr_prep_fds(&msg->msg, msg->buf, bufsize, |
217 | 0 | fds, num_fds); |
218 | 0 | } else { |
219 | 0 | fd_len = msghdr_prep_fds(NULL, NULL, bufsize, fds, num_fds); |
220 | 0 | } |
221 | |
|
222 | 0 | if (fd_len == -1) { |
223 | 0 | return -1; |
224 | 0 | } |
225 | | |
226 | 0 | if (bufsize >= (size_t)fd_len) { |
227 | 0 | bufsize -= fd_len; |
228 | 0 | } else { |
229 | 0 | bufsize = 0; |
230 | 0 | } |
231 | |
|
232 | 0 | if (msg != NULL) { |
233 | |
|
234 | 0 | if (addr != NULL) { |
235 | 0 | if (addrlen > sizeof(struct sockaddr_storage)) { |
236 | 0 | errno = EMSGSIZE; |
237 | 0 | return -1; |
238 | 0 | } |
239 | 0 | memcpy(&msg->addr, addr, addrlen); |
240 | 0 | msg->msg.msg_name = &msg->addr; |
241 | 0 | msg->msg.msg_namelen = addrlen; |
242 | 0 | } else { |
243 | 0 | msg->msg.msg_name = NULL; |
244 | 0 | msg->msg.msg_namelen = 0; |
245 | 0 | } |
246 | | |
247 | 0 | msg->iov.iov_base = msg->buf + fd_len; |
248 | 0 | msg->iov.iov_len = iov_buf( |
249 | 0 | iov, iovcnt, msg->iov.iov_base, bufsize); |
250 | 0 | iov_len = msg->iov.iov_len; |
251 | |
|
252 | 0 | msg->msg.msg_iov = &msg->iov; |
253 | 0 | msg->msg.msg_iovlen = 1; |
254 | 0 | } else { |
255 | 0 | iov_len = iov_buflen(iov, iovcnt); |
256 | 0 | } |
257 | | |
258 | 0 | needed = offsetof(struct msghdr_buf, buf) + fd_len; |
259 | 0 | if (needed < (size_t)fd_len) { |
260 | 0 | return -1; |
261 | 0 | } |
262 | 0 | needed += iov_len; |
263 | 0 | if (needed < iov_len) { |
264 | 0 | return -1; |
265 | 0 | } |
266 | | |
267 | 0 | return needed; |
268 | 0 | } |
269 | | |
270 | | struct msghdr *msghdr_buf_msghdr(struct msghdr_buf *msg) |
271 | 0 | { |
272 | 0 | return &msg->msg; |
273 | 0 | } |