]> granicus.if.org Git - strace/blob - tests/msg_control.c
msghdr.c: limit output when printing excessively large messages
[strace] / tests / msg_control.c
1 /*
2  * Check decoding of struct msghdr ancillary data.
3  *
4  * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "tests.h"
31 #include <errno.h>
32 #include <stddef.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/socket.h>
37 #include <net/if.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40
41 #ifndef SOL_IP
42 # define SOL_IP 0
43 #endif
44 #ifndef SOL_TCP
45 # define SOL_TCP 6
46 #endif
47
48 #ifndef SCM_SECURITY
49 # define SCM_SECURITY 3
50 #endif
51
52 #ifndef UIO_MAXIOV
53 # define UIO_MAXIOV 1024
54 #endif
55
56 #define MIN_SIZE_OF(type, member) \
57         (offsetof(type, member) + sizeof(((type *) 0)->member))
58
59 #define VAL_STR(val) val, #val
60
61 static struct cmsghdr *
62 get_cmsghdr(void *const page, const size_t len)
63 {
64         return page - CMSG_ALIGN(len);
65 }
66
67 #define DEFAULT_STRLEN 32
68
69 static void
70 print_fds(const struct cmsghdr *const cmsg, const size_t cmsg_len)
71 {
72         size_t nfd = cmsg_len > CMSG_LEN(0)
73                      ? (cmsg_len - CMSG_LEN(0)) / sizeof(int) : 0;
74         if (!nfd)
75                 return;
76
77         printf(", cmsg_data=[");
78         int *fdp = (int *) CMSG_DATA(cmsg);
79         size_t i;
80         for (i = 0; i < nfd; ++i) {
81                 if (i)
82                         printf(", ");
83 #ifndef VERBOSE_MSGHDR
84                 if (i >= DEFAULT_STRLEN) {
85                         printf("...");
86                         break;
87                 }
88 #endif
89                 printf("%d", fdp[i]);
90         }
91         printf("]");
92 }
93
94 static void
95 test_scm_rights1(struct msghdr *const mh,
96                  const size_t msg_controllen,
97                  void *const page,
98                  const void *const src,
99                  const size_t cmsg_len)
100 {
101         const size_t aligned_cms_len =
102                 cmsg_len > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len) : CMSG_LEN(0);
103         if (cmsg_len >= CMSG_LEN(0)
104             && aligned_cms_len + CMSG_LEN(0) <= msg_controllen)
105                 return;
106
107         struct cmsghdr *cmsg = get_cmsghdr(page, msg_controllen);
108
109         if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_len))
110                 cmsg->cmsg_len = cmsg_len;
111         if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_level))
112                 cmsg->cmsg_level = SOL_SOCKET;
113         if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_type))
114                 cmsg->cmsg_type = SCM_RIGHTS;
115
116         size_t src_len =
117                 cmsg_len < msg_controllen ? cmsg_len : msg_controllen;
118         if (src_len > CMSG_LEN(0))
119                 memcpy(CMSG_DATA(cmsg), src, src_len - CMSG_LEN(0));
120
121         mh->msg_control = cmsg;
122         mh->msg_controllen = msg_controllen;
123
124         int rc = sendmsg(-1, mh, 0);
125         int saved_errno = errno;
126
127         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
128                ", msg_iovlen=0");
129         if (msg_controllen < CMSG_LEN(0)) {
130                 if (msg_controllen)
131                         printf(", msg_control=%p", cmsg);
132         } else {
133                 printf(", msg_control=[{cmsg_len=%lu, cmsg_level=SOL_SOCKET"
134                        ", cmsg_type=SCM_RIGHTS", (unsigned long) cmsg_len);
135                 print_fds(cmsg, src_len);
136                 printf("}");
137                 if (aligned_cms_len < msg_controllen)
138                         printf(", %p", (void *) cmsg + aligned_cms_len);
139                 printf("]");
140         }
141
142         errno = saved_errno;
143         printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
144                (unsigned long) msg_controllen, rc, errno2name());
145 }
146
147 static void
148 test_scm_rights2(struct msghdr *const mh,
149                  const size_t msg_controllen,
150                  void *const page,
151                  const int *const *const src,
152                  const size_t *const cmsg_len)
153 {
154         const size_t aligned_cms_len[2] = {
155                 cmsg_len[0] > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len[0]) : CMSG_LEN(0),
156                 cmsg_len[1] > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len[1]) : CMSG_LEN(0)
157         };
158         if (cmsg_len[0] < CMSG_LEN(0)
159             || aligned_cms_len[0] + CMSG_LEN(0) > msg_controllen
160             || aligned_cms_len[0] + aligned_cms_len[1] + CMSG_LEN(0) <= msg_controllen)
161                 return;
162
163         struct cmsghdr *const cmsg[2] = {
164                 get_cmsghdr(page, msg_controllen),
165                 (void *) get_cmsghdr(page, msg_controllen) + aligned_cms_len[0]
166         };
167         cmsg[0]->cmsg_len = cmsg_len[0];
168         cmsg[0]->cmsg_level = SOL_SOCKET;
169         cmsg[0]->cmsg_type = SCM_RIGHTS;
170         if (cmsg_len[0] > CMSG_LEN(0))
171                 memcpy(CMSG_DATA(cmsg[0]), src[0], cmsg_len[0] - CMSG_LEN(0));
172
173         const size_t msg_controllen1 = msg_controllen - aligned_cms_len[0];
174         if (msg_controllen1 >= MIN_SIZE_OF(struct cmsghdr, cmsg_len))
175                 cmsg[1]->cmsg_len = cmsg_len[1];
176         if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_level))
177                 cmsg[1]->cmsg_level = SOL_SOCKET;
178         if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_type))
179                 cmsg[1]->cmsg_type = SCM_RIGHTS;
180         size_t src1_len =
181                 cmsg_len[1] < msg_controllen1 ? cmsg_len[1] : msg_controllen1;
182         if (src1_len > CMSG_LEN(0))
183                 memcpy(CMSG_DATA(cmsg[1]), src[1], src1_len - CMSG_LEN(0));
184
185         mh->msg_control = cmsg[0];
186         mh->msg_controllen = msg_controllen;
187
188         int rc = sendmsg(-1, mh, 0);
189         int saved_errno = errno;
190
191         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
192                ", msg_iovlen=0, msg_control=[{cmsg_len=%lu"
193                ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
194                (unsigned long) cmsg_len[0]);
195         print_fds(cmsg[0], cmsg_len[0]);
196         printf("}, {cmsg_len=%lu, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
197                (unsigned long) cmsg_len[1]);
198         print_fds(cmsg[1], src1_len);
199         printf("}");
200         if (aligned_cms_len[1] < msg_controllen1)
201                 printf(", %p", (void *) cmsg[1] + aligned_cms_len[1]);
202         printf("]");
203
204         errno = saved_errno;
205         printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
206                (unsigned long) msg_controllen, rc, errno2name());
207 }
208
209 static void
210 test_scm_rights3(struct msghdr *const mh, void *const page, const size_t nfds)
211 {
212         const size_t len = CMSG_SPACE(sizeof(int) * nfds);
213         struct cmsghdr *cmsg = get_cmsghdr(page, len);
214
215         cmsg->cmsg_len = CMSG_LEN(sizeof(int) * nfds);
216         cmsg->cmsg_level = SOL_SOCKET;
217         cmsg->cmsg_type = SCM_RIGHTS;
218         int *fdp = (int *) CMSG_DATA(cmsg);
219         size_t i;
220         for (i = 0; i < nfds; ++i)
221                 fdp[i] = i;
222
223         mh->msg_control = cmsg;
224         mh->msg_controllen = len;
225
226         int rc = sendmsg(-1, mh, 0);
227         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
228                ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
229                ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
230                (unsigned) cmsg->cmsg_len);
231         print_fds(cmsg, cmsg->cmsg_len);
232         printf("}], msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
233                (unsigned long) len, rc, errno2name());
234 }
235
236 static void
237 print_security(const struct cmsghdr *const cmsg, const size_t cmsg_len)
238 {
239         int n = cmsg_len > CMSG_LEN(0) ? cmsg_len - CMSG_LEN(0) : 0;
240         if (!n)
241                 return;
242
243         printf(", cmsg_data=\"%.*s\"", n, CMSG_DATA(cmsg));
244 }
245
246 static void
247 test_scm_security(struct msghdr *const mh,
248                   const size_t msg_controllen,
249                   void *const page,
250                   const void *const src,
251                   const size_t cmsg_len,
252                   const int cmsg_level,
253                   const char *const cmsg_level_str)
254 {
255         const size_t aligned_cms_len =
256                 cmsg_len > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len) : CMSG_LEN(0);
257         if (cmsg_len >= CMSG_LEN(0)
258             && aligned_cms_len + CMSG_LEN(0) <= msg_controllen)
259                 return;
260
261         struct cmsghdr *cmsg = get_cmsghdr(page, msg_controllen);
262
263         cmsg->cmsg_len = cmsg_len;
264         cmsg->cmsg_level = cmsg_level;
265         cmsg->cmsg_type = SCM_SECURITY;
266
267         size_t src_len =
268                 cmsg_len < msg_controllen ? cmsg_len : msg_controllen;
269         if (src_len > CMSG_LEN(0))
270                 memcpy(CMSG_DATA(cmsg), src, src_len - CMSG_LEN(0));
271
272         mh->msg_control = cmsg;
273         mh->msg_controllen = msg_controllen;
274
275         int rc = sendmsg(-1, mh, 0);
276         int saved_errno = errno;
277
278         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
279                ", msg_iovlen=0, msg_control=[{cmsg_len=%lu, cmsg_level=%s"
280                ", cmsg_type=SCM_SECURITY",
281                (unsigned long) cmsg_len, cmsg_level_str);
282         print_security(cmsg, src_len);
283         printf("}");
284         if (aligned_cms_len < msg_controllen)
285                 printf(", %p", (void *) cmsg + aligned_cms_len);
286         printf("]");
287
288         errno = saved_errno;
289         printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
290                (unsigned long) msg_controllen, rc, errno2name());
291 }
292
293 static void
294 test_unknown_type(struct msghdr *const mh,
295                   void *const page,
296                   const int cmsg_level,
297                   const char *const cmsg_level_str,
298                   const char *const cmsg_type_str)
299 {
300         struct cmsghdr *cmsg = get_cmsghdr(page, CMSG_LEN(0));
301
302         cmsg->cmsg_len = CMSG_LEN(0);
303         cmsg->cmsg_level = cmsg_level;
304         cmsg->cmsg_type = 0xfacefeed;
305
306         mh->msg_control = cmsg;
307         mh->msg_controllen = cmsg->cmsg_len;
308
309         int rc = sendmsg(-1, mh, 0);
310         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
311                ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=%s"
312                ", cmsg_type=%#x /* %s */}], msg_controllen=%u, msg_flags=0}"
313                ", 0) = %d %s (%m)\n",
314                (unsigned) cmsg->cmsg_len, cmsg_level_str, cmsg->cmsg_type,
315                cmsg_type_str, (unsigned) mh->msg_controllen, rc, errno2name());
316 }
317
318 static void
319 test_sol_socket(struct msghdr *const mh, void *const page)
320 {
321         static const int fds0[] = { -10, -11, -12, -13 };
322         static const int fds1[] = { -15, -16, -17, -18 };
323         size_t msg_controllen, max_msg_controllen;
324
325         max_msg_controllen = CMSG_SPACE(sizeof(fds0)) + sizeof(*fds0) - 1;
326         for (msg_controllen = 0;
327              msg_controllen <= max_msg_controllen;
328              msg_controllen++) {
329                 size_t cmsg_len;
330
331                 for (cmsg_len = 0;
332                      cmsg_len <= msg_controllen + CMSG_LEN(0);
333                      cmsg_len++) {
334                         test_scm_rights1(mh, msg_controllen,
335                                          page, fds0, cmsg_len);
336                 }
337         }
338
339         max_msg_controllen =
340                 CMSG_SPACE(sizeof(fds0)) + CMSG_SPACE(sizeof(fds1)) +
341                 sizeof(*fds0) - 1;
342         for (msg_controllen = CMSG_LEN(0) * 2;
343              msg_controllen <= max_msg_controllen;
344              msg_controllen++) {
345                 static const int *const fdps[] = { fds0, fds1 };
346                 size_t cmsg_len[2];
347
348                 for (cmsg_len[0] = CMSG_LEN(0);
349                      CMSG_ALIGN(cmsg_len[0]) + CMSG_LEN(0) <= msg_controllen
350                      && CMSG_ALIGN(cmsg_len[0]) <= CMSG_SPACE(sizeof(fds0));
351                      cmsg_len[0]++) {
352                         const size_t msg_controllen1 =
353                                 msg_controllen - CMSG_ALIGN(cmsg_len[0]);
354
355                         for (cmsg_len[1] = 0;
356                              cmsg_len[1] <= msg_controllen1 + CMSG_LEN(0);
357                              cmsg_len[1]++) {
358                                 test_scm_rights2(mh, msg_controllen,
359                                                  page, fdps, cmsg_len);
360                         }
361                 }
362         }
363
364         static const char text[16] = "0123456789abcdef";
365         max_msg_controllen = CMSG_SPACE(sizeof(text)) + CMSG_LEN(0) - 1;
366         for (msg_controllen = CMSG_LEN(0);
367              msg_controllen <= max_msg_controllen;
368              msg_controllen++) {
369                 size_t cmsg_len;
370
371                 for (cmsg_len = 0;
372                      cmsg_len <= msg_controllen + CMSG_LEN(0)
373                      && cmsg_len <= CMSG_LEN(sizeof(text));
374                      cmsg_len++) {
375                         test_scm_security(mh, msg_controllen,
376                                           page, text, cmsg_len,
377                                           VAL_STR(SOL_SOCKET));
378                 }
379         }
380
381         test_scm_rights3(mh, page, DEFAULT_STRLEN - 1);
382         test_scm_rights3(mh, page, DEFAULT_STRLEN);
383         test_scm_rights3(mh, page, DEFAULT_STRLEN + 1);
384
385         test_unknown_type(mh, page, VAL_STR(SOL_SOCKET), "SCM_???");
386 }
387
388 static void
389 test_ip_pktinfo(struct msghdr *const mh, void *const page,
390                 const int cmsg_type, const char *const cmsg_type_str)
391 {
392         const unsigned int len = CMSG_SPACE(sizeof(struct in_pktinfo));
393         struct cmsghdr *const cmsg = get_cmsghdr(page, len);
394
395         cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
396         cmsg->cmsg_level = SOL_IP;
397         cmsg->cmsg_type = cmsg_type;
398
399         struct in_pktinfo *const info = (struct in_pktinfo *) CMSG_DATA(cmsg);
400 #ifdef HAVE_IF_INDEXTONAME
401         info->ipi_ifindex = if_nametoindex("lo");
402 #else
403         info->ipi_ifindex = 1;
404 #endif
405         info->ipi_spec_dst.s_addr = inet_addr("1.2.3.4");
406         info->ipi_addr.s_addr = inet_addr("5.6.7.8");
407
408         mh->msg_control = cmsg;
409         mh->msg_controllen = len;
410
411         int rc = sendmsg(-1, mh, 0);
412         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
413                ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
414                ", cmsg_type=%s, cmsg_data={ipi_ifindex=%s"
415                ", ipi_spec_dst=inet_addr(\"%s\")"
416                ", ipi_addr=inet_addr(\"%s\")}}]"
417                ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
418                (unsigned) cmsg->cmsg_len, cmsg_type_str,
419 #ifdef HAVE_IF_INDEXTONAME
420                "if_nametoindex(\"lo\")",
421 #else
422                "1",
423 #endif
424                "1.2.3.4", "5.6.7.8", len, rc, errno2name());
425 }
426
427 static void
428 test_ip_uint(struct msghdr *const mh, void *const page,
429              const int cmsg_type, const char *const cmsg_type_str)
430 {
431         const unsigned int len = CMSG_SPACE(sizeof(int));
432         struct cmsghdr *const cmsg = get_cmsghdr(page, len);
433
434         cmsg->cmsg_len = CMSG_LEN(sizeof(int));
435         cmsg->cmsg_level = SOL_IP;
436         cmsg->cmsg_type = cmsg_type;
437
438         unsigned int *u = (void *) CMSG_DATA(cmsg);
439         *u = 0xfacefeed;
440
441         mh->msg_control = cmsg;
442         mh->msg_controllen = len;
443
444         int rc = sendmsg(-1, mh, 0);
445         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
446                ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
447                ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%u]}]"
448                ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
449                (unsigned) cmsg->cmsg_len, cmsg_type_str, *u, len,
450                rc, errno2name());
451 }
452
453 static void
454 test_ip_uint8_t(struct msghdr *const mh, void *const page,
455                 const int cmsg_type, const char *const cmsg_type_str)
456 {
457         const unsigned int len = CMSG_SPACE(1);
458         struct cmsghdr *const cmsg = get_cmsghdr(page, len);
459
460         cmsg->cmsg_len = CMSG_LEN(1);
461         cmsg->cmsg_level = SOL_IP;
462         cmsg->cmsg_type = cmsg_type;
463         *CMSG_DATA(cmsg) = 'A';
464
465         mh->msg_control = cmsg;
466         mh->msg_controllen = len;
467
468         int rc = sendmsg(-1, mh, 0);
469         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
470                ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
471                ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%#x]}]"
472                ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
473                (unsigned) cmsg->cmsg_len, cmsg_type_str,
474                (unsigned) (uint8_t) 'A', len, rc, errno2name());
475 }
476
477 static void
478 print_ip_opts(const void *const cmsg_data, const unsigned int data_len)
479 {
480         const unsigned char *const opts = cmsg_data;
481         unsigned int i;
482         for (i = 0; i < data_len; ++i) {
483                 if (i)
484                         printf(", ");
485 #ifndef VERBOSE_MSGHDR
486                 if (i >= DEFAULT_STRLEN) {
487                         printf("...");
488                         break;
489                 }
490 #endif
491                 printf("0x%02x", opts[i]);
492         }
493 }
494
495 static void
496 test_ip_opts(struct msghdr *const mh, void *const page,
497              const int cmsg_type, const char *const cmsg_type_str,
498              const unsigned int opts_len)
499 {
500         unsigned int len = CMSG_SPACE(opts_len);
501         struct cmsghdr *cmsg = get_cmsghdr(page, len);
502
503         cmsg->cmsg_len = CMSG_LEN(opts_len);
504         cmsg->cmsg_level = SOL_IP;
505         cmsg->cmsg_type = cmsg_type;
506         unsigned int i;
507         for (i = 0; i < opts_len; ++i)
508                 CMSG_DATA(cmsg)[i] = 'A' + i;
509
510         mh->msg_control = cmsg;
511         mh->msg_controllen = len;
512
513         int rc = sendmsg(-1, mh, 0);
514         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
515                ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
516                ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[",
517                (unsigned) cmsg->cmsg_len, cmsg_type_str);
518         print_ip_opts(CMSG_DATA(cmsg), opts_len);
519         printf("]}], msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
520                len, rc, errno2name());
521 }
522
523 #ifdef IP_CHECKSUM
524 struct sock_ee {
525         uint32_t ee_errno;
526         uint8_t  ee_origin;
527         uint8_t  ee_type;
528         uint8_t  ee_code;
529         uint8_t  ee_pad;
530         uint32_t ee_info;
531         uint32_t ee_data;
532         struct sockaddr_in offender;
533 };
534
535 static void
536 test_ip_recverr(struct msghdr *const mh, void *const page,
537                 const int cmsg_type, const char *const cmsg_type_str)
538 {
539         const unsigned int len = CMSG_SPACE(sizeof(struct sock_ee));
540         struct cmsghdr *const cmsg = get_cmsghdr(page, len);
541
542         cmsg->cmsg_len = CMSG_LEN(sizeof(struct sock_ee));
543         cmsg->cmsg_level = SOL_IP;
544         cmsg->cmsg_type = cmsg_type;
545
546         struct sock_ee *const e = (struct sock_ee *) CMSG_DATA(cmsg);
547         e->ee_errno = 0xdeadbeef;
548         e->ee_origin = 2;
549         e->ee_type = 3;
550         e->ee_code = 4;
551         e->ee_info = 0xfacefeed;
552         e->ee_data = 0xbadc0ded;
553         e->offender.sin_family = AF_INET,
554         e->offender.sin_port = htons(12345),
555         e->offender.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
556
557         mh->msg_control = cmsg;
558         mh->msg_controllen = len;
559
560         int rc = sendmsg(-1, mh, 0);
561         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
562                ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
563                ", cmsg_type=%s, cmsg_data={ee_errno=%u, ee_origin=%u"
564                ", ee_type=%u, ee_code=%u, ee_info=%u, ee_data=%u"
565                ", offender={sa_family=AF_INET, sin_port=htons(%hu)"
566                ", sin_addr=inet_addr(\"127.0.0.1\")}}}]"
567                ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
568                (unsigned) cmsg->cmsg_len, cmsg_type_str,
569                e->ee_errno, e->ee_origin, e->ee_type,
570                e->ee_code, e->ee_info, e->ee_data,
571                ntohs(e->offender.sin_port),
572                len, rc, errno2name());
573 }
574 #endif
575
576 #ifdef IP_ORIGDSTADDR
577 static void
578 test_ip_origdstaddr(struct msghdr *const mh, void *const page,
579                     const int cmsg_type, const char *const cmsg_type_str)
580 {
581         const unsigned int len = CMSG_SPACE(sizeof(struct sockaddr_in));
582         struct cmsghdr *const cmsg = get_cmsghdr(page, len);
583
584         cmsg->cmsg_len = CMSG_LEN(sizeof(struct sockaddr_in));
585         cmsg->cmsg_level = SOL_IP;
586         cmsg->cmsg_type = cmsg_type;
587
588         struct sockaddr_in *const sin = (struct sockaddr_in *) CMSG_DATA(cmsg);
589         sin->sin_family = AF_INET,
590         sin->sin_port = htons(12345),
591         sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
592
593         mh->msg_control = cmsg;
594         mh->msg_controllen = len;
595
596         int rc = sendmsg(-1, mh, 0);
597         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
598                ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
599                ", cmsg_type=%s, cmsg_data={sa_family=AF_INET"
600                ", sin_port=htons(%hu), sin_addr=inet_addr(\"127.0.0.1\")}}]"
601                ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
602                (unsigned) cmsg->cmsg_len, cmsg_type_str,
603                ntohs(sin->sin_port), len, rc, errno2name());
604 }
605 #endif
606
607 static void
608 test_sol_ip(struct msghdr *const mh, void *const page)
609 {
610         test_ip_pktinfo(mh, page, VAL_STR(IP_PKTINFO));
611         test_ip_uint(mh, page, VAL_STR(IP_TTL));
612         test_ip_uint8_t(mh, page, VAL_STR(IP_TOS));
613         test_ip_opts(mh, page, VAL_STR(IP_RECVOPTS), 1);
614         test_ip_opts(mh, page, VAL_STR(IP_RECVOPTS), 2);
615         test_ip_opts(mh, page, VAL_STR(IP_RECVOPTS), 3);
616         test_ip_opts(mh, page, VAL_STR(IP_RECVOPTS), 4);
617         test_ip_opts(mh, page, VAL_STR(IP_RETOPTS), 5);
618         test_ip_opts(mh, page, VAL_STR(IP_RETOPTS), 6);
619         test_ip_opts(mh, page, VAL_STR(IP_RETOPTS), 7);
620         test_ip_opts(mh, page, VAL_STR(IP_RETOPTS), 8);
621         test_ip_opts(mh, page, VAL_STR(IP_RETOPTS), DEFAULT_STRLEN - 1);
622         test_ip_opts(mh, page, VAL_STR(IP_RETOPTS), DEFAULT_STRLEN);
623         test_ip_opts(mh, page, VAL_STR(IP_RETOPTS), DEFAULT_STRLEN + 1);
624 #ifdef IP_CHECKSUM
625         test_ip_recverr(mh, page, VAL_STR(IP_RECVERR));
626 #endif
627 #ifdef IP_ORIGDSTADDR
628         test_ip_origdstaddr(mh, page, VAL_STR(IP_ORIGDSTADDR));
629 #endif
630 #ifdef IP_CHECKSUM
631         test_ip_uint(mh, page, VAL_STR(IP_CHECKSUM));
632 #endif
633         test_scm_security(mh, CMSG_LEN(0), page, 0, CMSG_LEN(0),
634                           VAL_STR(SOL_IP));
635         test_unknown_type(mh, page, VAL_STR(SOL_IP), "IP_???");
636 }
637
638 static void
639 test_unknown_level(struct msghdr *const mh, void *const page)
640 {
641         struct cmsghdr *cmsg = get_cmsghdr(page, CMSG_LEN(0));
642
643         cmsg->cmsg_len = CMSG_LEN(0);
644         cmsg->cmsg_level = SOL_TCP;
645         cmsg->cmsg_type = 0xdeadbeef;
646
647         mh->msg_control = cmsg;
648         mh->msg_controllen = cmsg->cmsg_len;
649
650         int rc = sendmsg(-1, mh, 0);
651         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
652                ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=%s"
653                ", cmsg_type=%#x}], msg_controllen=%u, msg_flags=0}"
654                ", 0) = %d %s (%m)\n",
655                (unsigned) cmsg->cmsg_len, "SOL_TCP", cmsg->cmsg_type,
656                (unsigned) mh->msg_controllen, rc, errno2name());
657 }
658
659 static void
660 test_big_len(struct msghdr *const mh)
661 {
662         int optmem_max;
663
664         if (read_int_from_file("/proc/sys/net/core/optmem_max", &optmem_max)
665             || optmem_max <= 0 || optmem_max > 0x100000)
666                 optmem_max = sizeof(long long) * (2 * UIO_MAXIOV + 512);
667         optmem_max = (optmem_max + sizeof(long long) - 1)
668                      & ~(sizeof(long long) - 1);
669
670         const size_t len = optmem_max * 2;
671         struct cmsghdr *const cmsg = tail_alloc(len);
672         cmsg->cmsg_len = len;
673         cmsg->cmsg_level = SOL_SOCKET;
674         cmsg->cmsg_type = SCM_RIGHTS;
675
676         mh->msg_control = cmsg;
677         mh->msg_controllen = len;
678
679         int rc = sendmsg(-1, mh, 0);
680         if (EBADF != errno)
681                 perror_msg_and_skip("sendmsg");
682
683         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
684                ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
685                ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
686                (unsigned) cmsg->cmsg_len);
687         print_fds(cmsg, optmem_max);
688         printf("}, ...], msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
689                (unsigned long) len, rc, errno2name());
690 }
691
692 int main(int ac, const char **av)
693 {
694         int rc = sendmsg(-1, 0, 0);
695         printf("sendmsg(-1, NULL, 0) = %d %s (%m)\n", rc, errno2name());
696
697         struct msghdr *mh = tail_alloc(sizeof(*mh));
698         memset(mh, 0, sizeof(*mh));
699         test_big_len(mh);
700
701         rc = sendmsg(-1, mh + 1, 0);
702         printf("sendmsg(-1, %p, 0) = %d %s (%m)\n",
703                mh + 1, rc, errno2name());
704
705         void *page = tail_alloc(1) + 1;
706         mh->msg_control = page;
707         mh->msg_controllen = CMSG_LEN(0);
708         rc = sendmsg(-1, mh, 0);
709         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
710                ", msg_iovlen=0, msg_control=%p, msg_controllen=%u"
711                ", msg_flags=0}, 0) = %d %s (%m)\n",
712                page, (unsigned) CMSG_LEN(0), rc, errno2name());
713
714         test_sol_socket(mh, page);
715         test_sol_ip(mh, page);
716         test_unknown_level(mh, page);
717
718         puts("+++ exited with 0 +++");
719         return 0;
720 }