Line data Source code
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 1104544 : ssize_t msghdr_prep_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize,
27 : const int *fds, size_t num_fds)
28 : {
29 1104544 : size_t fds_size = sizeof(int) * MIN(num_fds, INT8_MAX);
30 1104544 : size_t cmsg_len = CMSG_LEN(fds_size);
31 1104544 : size_t cmsg_space = CMSG_SPACE(fds_size);
32 139558 : struct cmsghdr *cmsg;
33 139558 : void *fdptr;
34 :
35 1104544 : if (num_fds == 0) {
36 964160 : if (msg != NULL) {
37 964160 : msg->msg_control = NULL;
38 964160 : msg->msg_controllen = 0;
39 : }
40 : /*
41 : * C99 doesn't allow 0-length arrays
42 : */
43 964160 : return 1;
44 : }
45 140384 : if (num_fds > INT8_MAX) {
46 0 : return -1;
47 : }
48 140384 : if ((msg == NULL) || (cmsg_space > bufsize)) {
49 : /*
50 : * C99 doesn't allow 0-length arrays
51 : */
52 70192 : return MAX(cmsg_space, 1);
53 : }
54 :
55 70192 : msg->msg_control = buf;
56 70192 : msg->msg_controllen = cmsg_space;
57 :
58 70192 : cmsg = CMSG_FIRSTHDR(msg);
59 70192 : cmsg->cmsg_level = SOL_SOCKET;
60 70192 : cmsg->cmsg_type = SCM_RIGHTS;
61 70192 : cmsg->cmsg_len = cmsg_len;
62 70192 : fdptr = CMSG_DATA(cmsg);
63 70192 : memcpy(fdptr, fds, fds_size);
64 70192 : msg->msg_controllen = cmsg->cmsg_len;
65 :
66 70192 : return cmsg_space;
67 : }
68 :
69 458694 : size_t msghdr_prep_recv_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize,
70 : size_t num_fds)
71 : {
72 458694 : size_t ret = CMSG_SPACE(sizeof(int) * num_fds);
73 :
74 458694 : if (bufsize < ret) {
75 160663 : return ret;
76 : }
77 229347 : if (msg != NULL) {
78 229347 : if (num_fds != 0) {
79 229347 : msg->msg_control = buf;
80 229347 : msg->msg_controllen = ret;
81 : } else {
82 0 : msg->msg_control = NULL;
83 0 : msg->msg_controllen = 0;
84 : }
85 : }
86 160663 : return ret;
87 : }
88 :
89 297991 : size_t msghdr_extract_fds(struct msghdr *msg, int *fds, size_t fds_size)
90 : {
91 68739 : struct cmsghdr *cmsg;
92 68739 : size_t num_fds;
93 :
94 297991 : for(cmsg = CMSG_FIRSTHDR(msg);
95 297991 : cmsg != NULL;
96 68739 : cmsg = CMSG_NXTHDR(msg, cmsg))
97 : {
98 137256 : if ((cmsg->cmsg_type == SCM_RIGHTS) &&
99 137146 : (cmsg->cmsg_level == SOL_SOCKET)) {
100 137146 : break;
101 : }
102 : }
103 :
104 297991 : if (cmsg == NULL) {
105 92106 : return 0;
106 : }
107 :
108 137256 : num_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
109 :
110 137256 : if ((num_fds != 0) && (fds != NULL) && (fds_size >= num_fds)) {
111 68628 : memcpy(fds, CMSG_DATA(cmsg), num_fds * sizeof(int));
112 : }
113 :
114 137146 : return num_fds;
115 : }
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 0 : 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 : {
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 : fds, num_fds);
218 : } else {
219 0 : fd_len = msghdr_prep_fds(NULL, NULL, bufsize, fds, num_fds);
220 : }
221 :
222 0 : if (fd_len == -1) {
223 0 : return -1;
224 : }
225 :
226 0 : if (bufsize >= (size_t)fd_len) {
227 0 : bufsize -= fd_len;
228 : } else {
229 0 : bufsize = 0;
230 : }
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 : }
239 0 : memcpy(&msg->addr, addr, addrlen);
240 0 : msg->msg.msg_name = &msg->addr;
241 0 : msg->msg.msg_namelen = addrlen;
242 : } else {
243 0 : msg->msg.msg_name = NULL;
244 0 : msg->msg.msg_namelen = 0;
245 : }
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 : } else {
255 0 : iov_len = iov_buflen(iov, iovcnt);
256 : }
257 :
258 0 : needed = offsetof(struct msghdr_buf, buf) + fd_len;
259 0 : if (needed < (size_t)fd_len) {
260 0 : return -1;
261 : }
262 0 : needed += iov_len;
263 0 : if (needed < iov_len) {
264 0 : return -1;
265 : }
266 :
267 0 : return needed;
268 : }
269 :
270 0 : struct msghdr *msghdr_buf_msghdr(struct msghdr_buf *msg)
271 : {
272 0 : return &msg->msg;
273 : }
|