Coverage Report

Created: 2026-09-03 07:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}