recvmsg01.c revision c57fba5535abf457e33dd7a986b6c512d95cdef6
1/* 2 * 3 * Copyright (c) International Business Machines Corp., 2001 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 2 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 13 * the 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, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20/* 21 * Test Name: recvmsg01 22 * 23 * Test Description: 24 * Verify that recvmsg() returns the proper errno for various failure cases 25 * 26 * Usage: <for command-line> 27 * recvmsg01 [-c n] [-e] [-i n] [-I x] [-P x] [-t] 28 * where, -c n : Run n copies concurrently. 29 * -e : Turn on errno logging. 30 * -i n : Execute test n times. 31 * -I x : Execute test for x seconds. 32 * -P x : Pause for x seconds between iterations. 33 * -t : Turn on syscall timing. 34 * 35 * HISTORY 36 * 07/2001 Ported by Wayne Boyer 37 * 38 * RESTRICTIONS: 39 * None. 40 * 41 */ 42 43/* The #ifndef code below is for 2.5 64-bit kernels, where */ 44/* the MSG_CMSG_COMPAT flag must be 0 in order for the syscall */ 45/* and this test to function correctly. */ 46#ifndef MSG_CMSG_COMPAT 47 48#if defined (__powerpc64__) || defined (__mips64) || defined (__x86_64__) || defined (__sparc64__) 49#define MSG_CMSG_COMPAT 0x80000000 50#else 51#define MSG_CMSG_COMPAT 0 52#endif 53 54#endif 55/***************************************************/ 56 57#include <stdio.h> 58#include <unistd.h> 59#include <string.h> 60#include <errno.h> 61#include <fcntl.h> 62 63#include <sys/types.h> 64#include <sys/socket.h> 65#include <sys/signal.h> 66#include <sys/uio.h> 67#include <sys/un.h> 68#include <sys/file.h> 69 70#include <netinet/in.h> 71 72#include "test.h" 73#include "usctest.h" 74 75char *TCID = "recvmsg01"; 76int testno; 77 78char buf[1024], cbuf[1024]; 79int s; /* socket descriptor */ 80int passed_fd = -1; /* rights-passing test descriptor */ 81struct sockaddr_in sin1, from; 82struct sockaddr_un sun1; 83struct msghdr msgdat; 84struct cmsghdr *control = 0; 85int controllen = 0; 86struct iovec iov[1]; 87static int sfd; /* shared between do_child and start_server */ 88static int ufd; /* shared between do_child and start_server */ 89 90void setup(void); 91void setup0(void); 92void setup1(void); 93void setup2(void); 94void setup3(void); 95void setup4(void); 96void cleanup(void); 97void cleanup0(void); 98void cleanup1(void); 99void cleanup2(void); 100void do_child(void); 101 102void sender(int); 103pid_t start_server(struct sockaddr_in *, struct sockaddr_un *); 104 105struct test_case_t { /* test case structure */ 106 int domain; /* PF_INET, PF_UNIX, ... */ 107 int type; /* SOCK_STREAM, SOCK_DGRAM ... */ 108 int proto; /* protocol number (usually 0 = default) */ 109 struct iovec *iov; 110 int iovcnt; 111 void *buf; /* recv data buffer */ 112 int buflen; /* recv buffer length */ 113 struct msghdr *msg; 114 unsigned flags; 115 struct sockaddr *from; /* from address */ 116 int fromlen; /* from address value/result buffer length */ 117 int retval; /* syscall return value */ 118 int experrno; /* expected errno */ 119 void (*setup) (void); 120 void (*cleanup) (void); 121 char *desc; 122} tdat[] = { 123/* 1 */ 124 { 125 PF_INET, SOCK_STREAM, 0, iov, 1, buf, sizeof(buf), &msgdat, 0, 126 (struct sockaddr *)&from, sizeof(from), 127 -1, EBADF, setup0, cleanup0, "bad file descriptor"} 128 , 129/* 2 */ 130 { 131 0, 0, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 0, 132 (struct sockaddr *)&from, sizeof(from), 133 -1, ENOTSOCK, setup0, cleanup0, "invalid socket"} 134 , 135/* 3 */ 136 { 137 PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), 138 &msgdat, 0, (struct sockaddr *)-1, sizeof(from), 0, 139 ENOTSOCK, setup1, cleanup1, "invalid socket buffer"} 140 , 141/* 4 */ 142 { 143 PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), 144 &msgdat, -1, (struct sockaddr *)&from, -1, -1, 145 EINVAL, setup1, cleanup1, "invalid socket length"}, 146/* 5 */ 147 { 148 PF_INET, SOCK_STREAM, 0, iov, 1, (void *)-1, sizeof(buf), 149 &msgdat, 0, (struct sockaddr *)&from, sizeof(from), 150 -1, EFAULT, setup1, cleanup1, "invalid recv buffer"} 151 , 152/* 6 */ 153 { 154 PF_INET, SOCK_STREAM, 0, 0, 1, (void *)buf, sizeof(buf), 155 &msgdat, 0, (struct sockaddr *)&from, sizeof(from), 156 -1, EFAULT, setup1, cleanup1, "invalid iovec buffer"} 157 , 158/* 7 */ 159 { 160 PF_INET, SOCK_STREAM, 0, iov, -1, (void *)buf, sizeof(buf), 161 &msgdat, 0, (struct sockaddr *)&from, sizeof(from), 162 -1, EMSGSIZE, setup1, cleanup1, "invalid iovec count"} 163 , 164/* 8 */ 165 { 166 PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), 167 &msgdat, 0, (struct sockaddr *)&from, sizeof(from), 168 0, 0, setup2, cleanup2, "rights reception"} 169 , 170/* 9 */ 171 { 172 PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), 173 &msgdat, ~MSG_CMSG_COMPAT, (struct sockaddr *)&from, 174 sizeof(from), -1, EINVAL, setup1, cleanup1, 175 "invalid flags set"} 176 , 177/* 10 */ 178 { 179 PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), 180 &msgdat, 0, (struct sockaddr *)&from, sizeof(from), 181 0, EINVAL, setup3, cleanup2, "invalid cmsg length"} 182 , 183/* 11 */ 184 { 185 PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), 186 &msgdat, 0, (struct sockaddr *)&from, sizeof(from), 187 0, 0, setup4, cleanup2, "large cmesg length"} 188,}; 189 190int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); 191 192int exp_enos[] = { EBADF, ENOTSOCK, EFAULT, EINVAL, 0 }; 193 194#ifdef UCLINUX 195static char *argv0; 196#endif 197 198int main(int argc, char *argv[]) 199{ 200 int lc; 201 char *msg; 202 203 if ((msg = parse_opts(argc, argv, NULL, NULL)) != NULL) 204 tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); 205#ifdef UCLINUX 206 argv0 = argv[0]; 207 maybe_run_child(&do_child, "dd", &sfd, &ufd); 208#endif 209 210 setup(); 211 212 TEST_EXP_ENOS(exp_enos); 213 214 for (lc = 0; TEST_LOOPING(lc); ++lc) { 215 tst_count = 0; 216 for (testno = 0; testno < TST_TOTAL; ++testno) { 217 tdat[testno].setup(); 218 219 /* setup common to all tests */ 220 221 iov[0].iov_base = tdat[testno].buf; 222 iov[0].iov_len = tdat[testno].buflen; 223 msgdat.msg_name = tdat[testno].from; 224 msgdat.msg_namelen = tdat[testno].fromlen; 225 msgdat.msg_iov = tdat[testno].iov; 226 msgdat.msg_iovlen = tdat[testno].iovcnt; 227 msgdat.msg_control = control; 228 msgdat.msg_controllen = controllen; 229 msgdat.msg_flags = 0; 230 231 TEST(recvmsg(s, tdat[testno].msg, tdat[testno].flags)); 232 if (TEST_RETURN >= 0) 233 TEST_RETURN = 0; /* all nonzero equal here */ 234 if (TEST_RETURN != tdat[testno].retval || 235 (TEST_RETURN < 0 && 236 TEST_ERRNO != tdat[testno].experrno)) { 237 tst_resm(TFAIL, "%s ; returned" 238 " %ld (expected %d), errno %d (expected" 239 " %d)", tdat[testno].desc, 240 TEST_RETURN, tdat[testno].retval, 241 TEST_ERRNO, tdat[testno].experrno); 242 } else { 243 TEST_ERROR_LOG(TEST_ERRNO); 244 tst_resm(TPASS, "%s successful", 245 tdat[testno].desc); 246 } 247 tdat[testno].cleanup(); 248 } 249 } 250 cleanup(); 251 252 tst_exit(); 253} 254 255pid_t pid; 256char tmpsunpath[1024]; 257 258void setup(void) 259{ 260 int tfd; 261 TEST_PAUSE; 262 263 tst_tmpdir(); 264 (void)strcpy(tmpsunpath, "udsockXXXXXX"); 265 tfd = mkstemp(tmpsunpath); 266 close(tfd); 267 unlink(tmpsunpath); 268 sun1.sun_family = AF_UNIX; 269 (void)strcpy(sun1.sun_path, tmpsunpath); 270 271 signal(SIGPIPE, SIG_IGN); 272 273 pid = start_server(&sin1, &sun1); 274} 275 276void cleanup(void) 277{ 278 if (pid > 0) 279 (void)kill(pid, SIGKILL); /* kill server */ 280 if (tmpsunpath[0] != '\0') 281 (void)unlink(tmpsunpath); 282 TEST_CLEANUP; 283 tst_rmdir(); 284 285} 286 287void setup0(void) 288{ 289 if (tdat[testno].experrno == EBADF) 290 s = 400; /* anything not an open file */ 291 else if ((s = open("/dev/null", O_WRONLY)) == -1) 292 tst_brkm(TBROK | TERRNO, cleanup, "open(/dev/null) failed"); 293} 294 295void cleanup0(void) 296{ 297 s = -1; 298} 299 300void setup1(void) 301{ 302 fd_set rdfds; 303 struct timeval timeout; 304 int n; 305 306 s = socket(tdat[testno].domain, tdat[testno].type, tdat[testno].proto); 307 if (s < 0) 308 tst_brkm(TBROK | TERRNO, cleanup, "socket setup failed"); 309 if (tdat[testno].type == SOCK_STREAM) { 310 if (tdat[testno].domain == PF_INET) { 311 if (connect(s, (struct sockaddr *)&sin1, sizeof(sin1)) < 312 0) 313 tst_brkm(TBROK | TERRNO, cleanup, 314 "connect failed"); 315 /* Wait for something to be readable, else we won't detect EFAULT on recv */ 316 FD_ZERO(&rdfds); 317 FD_SET(s, &rdfds); 318 timeout.tv_sec = 2; 319 timeout.tv_usec = 0; 320 n = select(s + 1, &rdfds, 0, 0, &timeout); 321 if (n != 1 || !FD_ISSET(s, &rdfds)) 322 tst_brkm(TBROK, cleanup, 323 "client setup1 failed - no message ready in 2 sec"); 324 } else if (tdat[testno].domain == PF_UNIX) { 325 if (connect(s, (struct sockaddr *)&sun1, sizeof(sun1)) < 326 0) 327 tst_brkm(TBROK | TERRNO, cleanup, 328 "UD connect failed"); 329 } 330 } 331} 332 333void setup2(void) 334{ 335 setup1(); 336 if (write(s, "R", 1) < 0) 337 tst_brkm(TBROK | TERRNO, cleanup, "test setup failed: write:"); 338 control = (struct cmsghdr *)cbuf; 339 controllen = control->cmsg_len = sizeof(cbuf); 340} 341 342void setup3(void) 343{ 344 setup2(); 345 controllen = sizeof(struct cmsghdr) - 1; 346} 347 348void setup4(void) 349{ 350 setup2(); 351 controllen = 128 * 1024; 352} 353 354void cleanup1(void) 355{ 356 (void)close(s); 357 close(ufd); 358 close(sfd); 359 s = -1; 360} 361 362void cleanup2(void) 363{ 364 close(ufd); 365 close(sfd); 366 (void)close(s); 367 s = -1; 368 369 if (passed_fd >= 0) 370 (void)close(passed_fd); 371 passed_fd = -1; 372 control = 0; 373 controllen = 0; 374} 375 376pid_t start_server(struct sockaddr_in *ssin, struct sockaddr_un *ssun) 377{ 378 pid_t pid; 379 socklen_t slen = sizeof(*ssin); 380 381 ssin->sin_family = AF_INET; 382 ssin->sin_port = 0; /* pick random free port */ 383 ssin->sin_addr.s_addr = INADDR_ANY; 384 385 /* set up inet socket */ 386 sfd = socket(PF_INET, SOCK_STREAM, 0); 387 if (sfd < 0) { 388 tst_brkm(TBROK | TERRNO, cleanup, "server socket failed"); 389 return -1; 390 } 391 if (bind(sfd, (struct sockaddr *)ssin, sizeof(*ssin)) < 0) { 392 tst_brkm(TBROK | TERRNO, cleanup, "server bind failed"); 393 return -1; 394 } 395 if (listen(sfd, 10) < 0) { 396 tst_brkm(TBROK | TERRNO, cleanup, "server listen failed"); 397 return -1; 398 } 399 if (getsockname(sfd, (struct sockaddr *)ssin, &slen) == -1) 400 tst_brkm(TBROK | TERRNO, cleanup, "getsockname failed"); 401 402 /* set up UNIX-domain socket */ 403 ufd = socket(PF_UNIX, SOCK_STREAM, 0); 404 if (ufd < 0) { 405 tst_brkm(TBROK | TERRNO, cleanup, "server UD socket failed"); 406 return -1; 407 } 408 if (bind(ufd, (struct sockaddr *)ssun, sizeof(*ssun))) { 409 tst_brkm(TBROK | TERRNO, cleanup, "server UD bind failed"); 410 return -1; 411 } 412 if (listen(ufd, 10) < 0) { 413 tst_brkm(TBROK | TERRNO, cleanup, "server UD listen failed"); 414 return -1; 415 } 416 417 switch ((pid = FORK_OR_VFORK())) { 418 case 0: /* child */ 419#ifdef UCLINUX 420 if (self_exec(argv0, "dd", sfd, ufd) < 0) 421 tst_brkm(TBROK | TERRNO, cleanup, 422 "server self_exec failed"); 423#else 424 do_child(); 425#endif 426 break; 427 case -1: 428 tst_brkm(TBROK | TERRNO, cleanup, "server fork failed"); 429 /* fall through */ 430 default: /* parent */ 431 (void)close(sfd); 432 (void)close(ufd); 433 return pid; 434 } 435 exit(1); 436} 437 438void do_child(void) 439{ 440 struct sockaddr_in fsin; 441 struct sockaddr_un fsun; 442 fd_set afds, rfds; 443 int nfds, cc, fd; 444 445 FD_ZERO(&afds); 446 FD_SET(sfd, &afds); 447 FD_SET(ufd, &afds); 448 449 nfds = getdtablesize(); 450 451 /* accept connections until killed */ 452 while (1) { 453 socklen_t fromlen; 454 455 memcpy(&rfds, &afds, sizeof(rfds)); 456 457 if (select(nfds, &rfds, (fd_set *) 0, (fd_set *) 0, 458 (struct timeval *)0) < 0) { 459 if (errno != EINTR) { 460 perror("server select"); 461 exit(1); 462 } 463 continue; 464 } 465 if (FD_ISSET(sfd, &rfds)) { 466 int newfd; 467 468 fromlen = sizeof(fsin); 469 newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen); 470 if (newfd >= 0) { 471 FD_SET(newfd, &afds); 472 /* send something back */ 473 (void)write(newfd, "hoser\n", 6); 474 } 475 } 476 if (FD_ISSET(ufd, &rfds)) { 477 int newfd; 478 479 fromlen = sizeof(fsun); 480 newfd = accept(ufd, (struct sockaddr *)&fsun, &fromlen); 481 if (newfd >= 0) 482 FD_SET(newfd, &afds); 483 } 484 for (fd = 0; fd < nfds; ++fd) 485 if (fd != sfd && fd != ufd && FD_ISSET(fd, &rfds)) { 486 char rbuf[1024]; 487 488 cc = read(fd, rbuf, sizeof(rbuf)); 489 if (cc && rbuf[0] == 'R') 490 sender(fd); 491 if (cc == 0 || (cc < 0 && errno != EINTR)) { 492 (void)close(fd); 493 FD_CLR(fd, &afds); 494 } 495 } 496 } 497} 498 499#define TM "from recvmsg01 server" 500 501/* special for rights-passing test */ 502void sender(int fd) 503{ 504 struct msghdr mh; 505 struct cmsghdr *control; 506 char tmpfn[1024], snd_cbuf[1024]; 507 int tfd; 508 509 (void)strcpy(tmpfn, "smtXXXXXX"); 510 tfd = mkstemp(tmpfn); 511 if (tfd < 0) 512 return; 513 514 memset(&mh, 0x00, sizeof(mh)); 515 516 /* set up cmsghdr */ 517 control = (struct cmsghdr *)snd_cbuf; 518 memset(control, 0x00, sizeof(struct cmsghdr)); 519 control->cmsg_len = sizeof(struct cmsghdr) + 4; 520 control->cmsg_level = SOL_SOCKET; 521 control->cmsg_type = SCM_RIGHTS; 522 *(int *)CMSG_DATA(control) = tfd; 523 524 /* set up msghdr */ 525 iov[0].iov_base = TM; 526 iov[0].iov_len = sizeof(TM); 527 mh.msg_iov = iov; 528 mh.msg_iovlen = 1; 529 mh.msg_flags = 0; 530 mh.msg_control = control; 531 mh.msg_controllen = control->cmsg_len; 532 533 /* do it */ 534 (void)sendmsg(fd, &mh, 0); 535 (void)close(tfd); 536 (void)unlink(tmpfn); 537} 538