replace: Add check for variable program_invocation_short_name
[bbaumbach/samba-autobuild/.git] / lib / util / msghdr.c
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 {
29         size_t fds_size = sizeof(int) * MIN(num_fds, INT8_MAX);
30         size_t cmsg_len = CMSG_LEN(fds_size);
31         size_t cmsg_space = CMSG_SPACE(fds_size);
32         struct cmsghdr *cmsg;
33         void *fdptr;
34
35         if (num_fds == 0) {
36                 if (msg != NULL) {
37                         msg->msg_control = NULL;
38                         msg->msg_controllen = 0;
39                 }
40                 /*
41                  * C99 doesn't allow 0-length arrays
42                  */
43                 return 1;
44         }
45         if (num_fds > INT8_MAX) {
46                 return -1;
47         }
48         if ((msg == NULL) || (cmsg_space > bufsize)) {
49                 /*
50                  * C99 doesn't allow 0-length arrays
51                  */
52                 return MAX(cmsg_space, 1);
53         }
54
55         msg->msg_control = buf;
56         msg->msg_controllen = cmsg_space;
57
58         cmsg = CMSG_FIRSTHDR(msg);
59         cmsg->cmsg_level = SOL_SOCKET;
60         cmsg->cmsg_type = SCM_RIGHTS;
61         cmsg->cmsg_len = cmsg_len;
62         fdptr = CMSG_DATA(cmsg);
63         memcpy(fdptr, fds, fds_size);
64         msg->msg_controllen = cmsg->cmsg_len;
65
66         return cmsg_space;
67 }
68
69 size_t msghdr_prep_recv_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize,
70                             size_t num_fds)
71 {
72         size_t ret = CMSG_SPACE(sizeof(int) * num_fds);
73
74         if (bufsize < ret) {
75                 return ret;
76         }
77         if (msg != NULL) {
78                 if (num_fds != 0) {
79                         msg->msg_control = buf;
80                         msg->msg_controllen = ret;
81                 } else {
82                         msg->msg_control = NULL;
83                         msg->msg_controllen = 0;
84                 }
85         }
86         return ret;
87 }
88
89 size_t msghdr_extract_fds(struct msghdr *msg, int *fds, size_t fds_size)
90 {
91         struct cmsghdr *cmsg;
92         size_t num_fds;
93
94         for(cmsg = CMSG_FIRSTHDR(msg);
95             cmsg != NULL;
96             cmsg = CMSG_NXTHDR(msg, cmsg))
97         {
98                 if ((cmsg->cmsg_type == SCM_RIGHTS) &&
99                     (cmsg->cmsg_level == SOL_SOCKET)) {
100                         break;
101                 }
102         }
103
104         if (cmsg == NULL) {
105                 return 0;
106         }
107
108         num_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
109
110         if ((num_fds != 0) && (fds != NULL) && (fds_size >= num_fds)) {
111                 memcpy(fds, CMSG_DATA(cmsg), num_fds * sizeof(int));
112         }
113
114         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 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         ssize_t fd_len;
208         size_t iov_len, needed, bufsize;
209
210         bufsize = (msgsize > offsetof(struct msghdr_buf, buf)) ?
211                 msgsize - offsetof(struct msghdr_buf, buf) : 0;
212
213         if (msg != NULL) {
214                 msg->msg = (struct msghdr) { 0 };
215
216                 fd_len = msghdr_prep_fds(&msg->msg, msg->buf, bufsize,
217                                          fds, num_fds);
218         } else {
219                 fd_len = msghdr_prep_fds(NULL, NULL, bufsize, fds, num_fds);
220         }
221
222         if (fd_len == -1) {
223                 return -1;
224         }
225
226         if (bufsize >= fd_len) {
227                 bufsize -= fd_len;
228         } else {
229                 bufsize = 0;
230         }
231
232         if (msg != NULL) {
233
234                 if (addr != NULL) {
235                         if (addrlen > sizeof(struct sockaddr_storage)) {
236                                 errno = EMSGSIZE;
237                                 return -1;
238                         }
239                         memcpy(&msg->addr, addr, addrlen);
240                         msg->msg.msg_name = &msg->addr;
241                         msg->msg.msg_namelen = addrlen;
242                 } else {
243                         msg->msg.msg_name = NULL;
244                         msg->msg.msg_namelen = 0;
245                 }
246
247                 msg->iov.iov_base = msg->buf + fd_len;
248                 msg->iov.iov_len = iov_buf(
249                         iov, iovcnt, msg->iov.iov_base, bufsize);
250                 iov_len = msg->iov.iov_len;
251
252                 msg->msg.msg_iov = &msg->iov;
253                 msg->msg.msg_iovlen = 1;
254         } else {
255                 iov_len = iov_buflen(iov, iovcnt);
256         }
257
258         needed = offsetof(struct msghdr_buf, buf) + fd_len;
259         if (needed < fd_len) {
260                 return -1;
261         }
262         needed += iov_len;
263         if (needed < iov_len) {
264                 return -1;
265         }
266
267         return needed;
268 }
269
270 struct msghdr *msghdr_buf_msghdr(struct msghdr_buf *msg)
271 {
272         return &msg->msg;
273 }