lib: Move msghdr to lib/util/
[nivanova/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                 return 0;
41         }
42         if (num_fds > INT8_MAX) {
43                 return -1;
44         }
45         if ((msg == NULL) || (cmsg_space > bufsize)) {
46                 return cmsg_space;
47         }
48
49         msg->msg_control = buf;
50         msg->msg_controllen = cmsg_space;
51
52         cmsg = CMSG_FIRSTHDR(msg);
53         cmsg->cmsg_level = SOL_SOCKET;
54         cmsg->cmsg_type = SCM_RIGHTS;
55         cmsg->cmsg_len = cmsg_len;
56         fdptr = CMSG_DATA(cmsg);
57         memcpy(fdptr, fds, fds_size);
58         msg->msg_controllen = cmsg->cmsg_len;
59
60         return cmsg_space;
61 }
62
63 size_t msghdr_prep_recv_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize,
64                             size_t num_fds)
65 {
66         size_t ret = CMSG_SPACE(sizeof(int) * num_fds);
67
68         if (bufsize < ret) {
69                 return ret;
70         }
71         if (msg != NULL) {
72                 if (num_fds != 0) {
73                         msg->msg_control = buf;
74                         msg->msg_controllen = ret;
75                 } else {
76                         msg->msg_control = NULL;
77                         msg->msg_controllen = 0;
78                 }
79         }
80         return ret;
81 }
82
83 size_t msghdr_extract_fds(struct msghdr *msg, int *fds, size_t fds_size)
84 {
85         struct cmsghdr *cmsg;
86         size_t num_fds;
87
88         for(cmsg = CMSG_FIRSTHDR(msg);
89             cmsg != NULL;
90             cmsg = CMSG_NXTHDR(msg, cmsg))
91         {
92                 if ((cmsg->cmsg_type == SCM_RIGHTS) &&
93                     (cmsg->cmsg_level == SOL_SOCKET)) {
94                         break;
95                 }
96         }
97
98         if (cmsg == NULL) {
99                 return 0;
100         }
101
102         num_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
103
104         if ((num_fds != 0) && (fds != NULL) && (fds_size >= num_fds)) {
105                 memcpy(fds, CMSG_DATA(cmsg), num_fds * sizeof(int));
106         }
107
108         return num_fds;
109 }
110
111 #elif defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS)
112
113 ssize_t msghdr_prep_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize,
114                         const int *fds, size_t num_fds)
115 {
116         size_t needed;
117
118         if (num_fds > INT8_MAX) {
119                 return -1;
120         }
121
122         needed = sizeof(int) * num_fds;
123
124         if ((msg == NULL) || (needed > bufsize)) {
125                 return needed;
126         }
127
128         memcpy(buf, fds, needed);
129
130         msg->msg_accrights = (caddr_t) buf;
131         msg->msg_accrightslen = needed;
132
133         return needed;
134 }
135
136 size_t msghdr_prep_recv_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize,
137                             size_t num_fds)
138 {
139         size_t ret = num_fds * sizeof(int);
140
141         if (bufsize < ret) {
142                 return ret;
143         }
144
145         if (msg != NULL) {
146                 if (num_fds != 0) {
147                         msg->msg_accrights = (caddr_t) buf;
148                         msg->msg_accrightslen = ret;
149                 } else {
150                         msg->msg_accrights = NULL;
151                         msg->msg_accrightslen = 0;
152                 }
153         }
154         return ret;
155 }
156
157 size_t msghdr_extract_fds(struct msghdr *msg, int *fds, size_t fds_size)
158 {
159         size_t num_fds = msg->msg_accrightslen / sizeof(int);
160
161         if ((fds != 0) && (num_fds <= fds_size)) {
162                 memcpy(fds, msg->msg_accrights, msg->msg_accrightslen);
163         }
164
165         return num_fds;
166 }
167
168 #else
169
170 ssize_t msghdr_prep_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize,
171                         const int *fds, size_t num_fds)
172 {
173         return -1;
174 }
175
176 size_t msghdr_prep_recv_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize,
177                             size_t num_fds)
178 {
179         return 0;
180 }
181
182 size_t msghdr_extract_fds(struct msghdr *msg, int *fds, size_t fds_size)
183 {
184         return 0;
185 }
186
187 #endif
188
189 struct msghdr_buf {
190         struct msghdr msg;
191         struct sockaddr_storage addr;
192         struct iovec iov;
193         uint8_t buf[];
194 };
195
196 ssize_t msghdr_copy(struct msghdr_buf *msg, size_t msgsize,
197                     const void *addr, socklen_t addrlen,
198                     const struct iovec *iov, int iovcnt,
199                     const int *fds, size_t num_fds)
200 {
201         ssize_t fd_len;
202         size_t iov_len, needed, bufsize;
203
204         bufsize = (msgsize > offsetof(struct msghdr_buf, buf)) ?
205                 msgsize - offsetof(struct msghdr_buf, buf) : 0;
206
207         fd_len = msghdr_prep_fds(&msg->msg, msg->buf, bufsize, fds, num_fds);
208
209         if (fd_len == -1) {
210                 return -1;
211         }
212
213         if (bufsize >= fd_len) {
214                 bufsize -= fd_len;
215         } else {
216                 bufsize = 0;
217         }
218
219         if (msg != NULL) {
220
221                 if (addr != NULL) {
222                         if (addrlen > sizeof(struct sockaddr_storage)) {
223                                 errno = EMSGSIZE;
224                                 return -1;
225                         }
226                         memcpy(&msg->addr, addr, addrlen);
227                         msg->msg.msg_name = &msg->addr;
228                         msg->msg.msg_namelen = addrlen;
229                 } else {
230                         msg->msg.msg_name = NULL;
231                         msg->msg.msg_namelen = 0;
232                 }
233
234                 msg->iov.iov_base = msg->buf + fd_len;
235                 msg->iov.iov_len = iov_buf(
236                         iov, iovcnt, msg->iov.iov_base, bufsize);
237                 iov_len = msg->iov.iov_len;
238
239                 msg->msg.msg_iov = &msg->iov;
240                 msg->msg.msg_iovlen = 1;
241         } else {
242                 iov_len = iov_buflen(iov, iovcnt);
243         }
244
245         needed = offsetof(struct msghdr_buf, buf) + fd_len;
246         if (needed < fd_len) {
247                 return -1;
248         }
249         needed += iov_len;
250         if (needed < iov_len) {
251                 return -1;
252         }
253
254         return needed;
255 }
256
257 struct msghdr *msghdr_buf_msghdr(struct msghdr_buf *msg)
258 {
259         return &msg->msg;
260 }