82f7ca7cdee18e2be7cfac0f13dd9ea989476f2a
[obnox/samba/samba-obnox.git] / source3 / lib / 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/msghdr.h"
21 #include "lib/iov_buf.h"
22 #include <sys/socket.h>
23
24 ssize_t msghdr_prep_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize,
25                         const int *fds, size_t num_fds)
26 {
27         size_t fds_size = sizeof(int) * MIN(num_fds, INT8_MAX);
28         size_t cmsg_len = CMSG_LEN(fds_size);
29         size_t cmsg_space = CMSG_SPACE(fds_size);
30         struct cmsghdr *cmsg;
31         void *fdptr;
32
33         if (num_fds == 0) {
34                 if (msg != NULL) {
35                         msg->msg_control = NULL;
36                         msg->msg_controllen = 0;
37                 }
38                 return 0;
39         }
40         if (num_fds > INT8_MAX) {
41                 return -1;
42         }
43         if ((msg == NULL) || (cmsg_space > bufsize)) {
44                 return cmsg_space;
45         }
46
47         msg->msg_control = buf;
48         msg->msg_controllen = cmsg_space;
49
50         cmsg = CMSG_FIRSTHDR(msg);
51         cmsg->cmsg_level = SOL_SOCKET;
52         cmsg->cmsg_type = SCM_RIGHTS;
53         cmsg->cmsg_len = cmsg_len;
54         fdptr = CMSG_DATA(cmsg);
55         memcpy(fdptr, fds, fds_size);
56         msg->msg_controllen = cmsg->cmsg_len;
57
58         return cmsg_space;
59 }
60
61 struct msghdr_buf {
62         struct msghdr msg;
63         struct sockaddr_storage addr;
64         struct iovec iov;
65         uint8_t buf[];
66 };
67
68 ssize_t msghdr_copy(struct msghdr_buf *msg, size_t msgsize,
69                     const void *addr, socklen_t addrlen,
70                     const struct iovec *iov, int iovcnt,
71                     const int *fds, size_t num_fds)
72 {
73         size_t fd_len, iov_len, needed, bufsize;
74
75         bufsize = (msgsize > offsetof(struct msghdr_buf, buf)) ?
76                 msgsize - offsetof(struct msghdr_buf, buf) : 0;
77
78         fd_len = msghdr_prep_fds(&msg->msg, msg->buf, bufsize, fds, num_fds);
79
80         if (bufsize >= fd_len) {
81                 bufsize -= fd_len;
82         } else {
83                 bufsize = 0;
84         }
85
86         if (msg != NULL) {
87
88                 if (addr != NULL) {
89                         if (addrlen > sizeof(struct sockaddr_storage)) {
90                                 errno = EMSGSIZE;
91                                 return -1;
92                         }
93                         memcpy(&msg->addr, addr, addrlen);
94                         msg->msg.msg_name = &msg->addr;
95                         msg->msg.msg_namelen = addrlen;
96                 } else {
97                         msg->msg.msg_name = NULL;
98                         msg->msg.msg_namelen = 0;
99                 }
100
101                 msg->iov.iov_base = msg->buf + fd_len;
102                 msg->iov.iov_len = iov_buf(
103                         iov, iovcnt, msg->iov.iov_base, bufsize);
104                 iov_len = msg->iov.iov_len;
105
106                 msg->msg.msg_iov = &msg->iov;
107                 msg->msg.msg_iovlen = 1;
108         } else {
109                 iov_len = iov_buflen(iov, iovcnt);
110         }
111
112         needed = offsetof(struct msghdr_buf, buf) + fd_len;
113         if (needed < fd_len) {
114                 return -1;
115         }
116         needed += iov_len;
117         if (needed < iov_len) {
118                 return -1;
119         }
120
121         return needed;
122 }
123
124 struct msghdr *msghdr_buf_msghdr(struct msghdr_buf *msg)
125 {
126         return &msg->msg;
127 }
128
129 size_t msghdr_prep_recv_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize,
130                             size_t num_fds)
131 {
132         size_t ret = CMSG_SPACE(sizeof(int) * num_fds);
133
134         if (bufsize < ret) {
135                 return ret;
136         }
137         if (msg != NULL) {
138                 if (num_fds != 0) {
139                         msg->msg_control = buf;
140                         msg->msg_controllen = ret;
141                 } else {
142                         msg->msg_control = NULL;
143                         msg->msg_controllen = 0;
144                 }
145         }
146         return ret;
147 }
148
149 size_t msghdr_extract_fds(struct msghdr *msg, int *fds, size_t fds_size)
150 {
151         struct cmsghdr *cmsg;
152         size_t num_fds;
153
154         for(cmsg = CMSG_FIRSTHDR(msg);
155             cmsg != NULL;
156             cmsg = CMSG_NXTHDR(msg, cmsg))
157         {
158                 if ((cmsg->cmsg_type == SCM_RIGHTS) &&
159                     (cmsg->cmsg_level == SOL_SOCKET)) {
160                         break;
161                 }
162         }
163
164         if (cmsg == NULL) {
165                 return 0;
166         }
167
168         num_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
169
170         if ((num_fds != 0) && (fds != NULL) && (fds_size >= num_fds)) {
171                 memcpy(fds, CMSG_DATA(cmsg), num_fds * sizeof(int));
172         }
173
174         return num_fds;
175 }