recvmsg01.c revision d34d581c6a320e356a6cda923c7aa399479e812c
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 (__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 58#include <stdio.h> 59#include <unistd.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"; /* Test program identifier. */ 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 { PF_INET, SOCK_STREAM, 0, iov, 1, buf, sizeof(buf), &msgdat, 0, 125 (struct sockaddr *)&from, sizeof(from), 126 -1, EBADF, setup0, cleanup0, "bad file descriptor" }, 127/* 2 */ 128 { 0, 0, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 0, 129 (struct sockaddr *)&from, sizeof(from), 130 -1, ENOTSOCK, setup0, cleanup0, "invalid socket" }, 131/* 3 */ 132 { PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 0, 133 (struct sockaddr *)-1, sizeof(from), 134 0, ENOTSOCK, setup1, cleanup1, "invalid socket buffer" }, 135/* 4 */ 136 { PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, -1, 137 (struct sockaddr *)&from, -1, 138 -1, EINVAL, setup1, cleanup1, "invalid socket length" }, 139/* 5 */ 140 { PF_INET, SOCK_STREAM, 0, iov, 1, (void *)-1, sizeof(buf), &msgdat, 0, 141 (struct sockaddr *)&from, sizeof(from), 142 -1, EFAULT, setup1, cleanup1, "invalid recv buffer" }, 143/* 6 */ 144 { PF_INET, SOCK_STREAM, 0, 0, 1, (void *)buf, sizeof(buf), &msgdat, 0, 145 (struct sockaddr *)&from, sizeof(from), 146 -1, EFAULT, setup1, cleanup1, "invalid iovec buffer" }, 147/* 7 */ 148 { PF_INET, SOCK_STREAM, 0, iov, -1, (void *)buf, sizeof(buf), &msgdat,0, 149 (struct sockaddr *)&from, sizeof(from), 150 -1, EMSGSIZE, setup1, cleanup1, "invalid iovec count" }, 151/* 8 */ 152 { PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat,0, 153 (struct sockaddr *)&from, sizeof(from), 154 0, 0, setup2, cleanup2, "rights reception" }, 155/* 9 */ 156 { PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat,~MSG_CMSG_COMPAT, 157 (struct sockaddr *)&from, sizeof(from), 158 -1, EINVAL, setup1, cleanup1, "invalid flags set" }, 159/* 10 */ 160 { PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat,0, 161 (struct sockaddr *)&from, sizeof(from), 162 0, EINVAL, setup3, cleanup2, "invalid cmsg length" }, 163/* 11 */ 164 { PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 165 0, (struct sockaddr *)&from, sizeof(from), 166 0, 0, setup4, cleanup2, "large cmesg length" }, 167}; 168 169int TST_TOTAL=sizeof(tdat)/sizeof(tdat[0]); /* Total number of test cases. */ 170 171int exp_enos[] = {EBADF, ENOTSOCK, EFAULT, EINVAL, 0}; 172 173extern int Tst_count; 174 175#ifdef UCLINUX 176static char *argv0; 177#endif 178 179int 180main(int argc, char *argv[]) 181{ 182 int lc; /* loop counter */ 183 char *msg; /* message returned from parse_opts */ 184 185 /* Parse standard options given to run the test. */ 186 msg = parse_opts(argc, argv, (option_t *)NULL, NULL); 187 if (msg != (char *)NULL) { 188 tst_brkm(TBROK, tst_exit, "OPTION PARSING ERROR - %s", msg); 189 } 190 191#ifdef UCLINUX 192 argv0 = argv[0]; 193 maybe_run_child(&do_child, "dd", &sfd, &ufd); 194#endif 195 196 setup(); 197 198 TEST_EXP_ENOS(exp_enos); 199 200 /* Check looping state if -i option given */ 201 for (lc = 0; TEST_LOOPING(lc); ++lc) { 202 Tst_count = 0; 203 for (testno=0; testno < TST_TOTAL; ++testno) { 204 tdat[testno].setup(); 205 206 /* setup common to all tests */ 207 208 iov[0].iov_base = tdat[testno].buf; 209 iov[0].iov_len = tdat[testno].buflen; 210 msgdat.msg_name = tdat[testno].from; 211 msgdat.msg_namelen = tdat[testno].fromlen; 212 msgdat.msg_iov = tdat[testno].iov; 213 msgdat.msg_iovlen = tdat[testno].iovcnt; 214 msgdat.msg_control = control; 215 msgdat.msg_controllen = controllen; 216 msgdat.msg_flags = 0; 217 218 TEST(recvmsg(s, tdat[testno].msg, tdat[testno].flags)); 219 if (TEST_RETURN >= 0) 220 TEST_RETURN = 0; /* all nonzero equal here */ 221 if (TEST_RETURN != tdat[testno].retval || 222 (TEST_RETURN < 0 && 223 TEST_ERRNO != tdat[testno].experrno)) { 224 tst_resm(TFAIL, "%s ; returned" 225 " %d (expected %d), errno %d (expected" 226 " %d)", tdat[testno].desc, 227 TEST_RETURN, tdat[testno].retval, 228 TEST_ERRNO, tdat[testno].experrno); 229 } else { 230 TEST_ERROR_LOG(TEST_ERRNO); 231 tst_resm(TPASS, "%s successful", 232 tdat[testno].desc); 233 } 234 tdat[testno].cleanup(); 235 } 236 } 237 cleanup(); 238 /*NOT REACHED*/ 239 return 0; 240} /* End main */ 241 242pid_t pid; 243char tmpsunpath[1024]; 244 245void 246setup(void) 247{ 248 int tfd; 249 TEST_PAUSE; /* if -P option specified */ 250 251 /* initialize sockaddr's */ 252 sin1.sin_family = AF_INET; 253 sin1.sin_port = htons((getpid() % 32768) +11000); 254 sin1.sin_addr.s_addr = INADDR_ANY; 255 tst_tmpdir(); 256 (void) strcpy(tmpsunpath, "udsockXXXXXX"); 257 tfd = mkstemp(tmpsunpath); 258 close(tfd); 259 unlink(tmpsunpath); 260 sun1.sun_family = AF_UNIX; 261 (void) strcpy(sun1.sun_path, tmpsunpath); 262 263 signal(SIGPIPE, SIG_IGN); 264 265 pid = start_server(&sin1, &sun1); 266} 267 268void 269cleanup(void) 270{ 271 (void) kill(pid, SIGKILL); /* kill server */ 272 if (tmpsunpath[0] != '\0') 273 (void) unlink(tmpsunpath); 274 TEST_CLEANUP; 275 tst_rmdir(); 276 tst_exit(); 277} 278 279 280void 281setup0(void) 282{ 283 if (tdat[testno].experrno == EBADF) 284 s = 400; /* anything not an open file */ 285 else 286 if((s = open("/dev/null", O_WRONLY)) == -1) 287 tst_brkm(TBROK, cleanup, "error opening /dev/null - " 288 "errno: %s", strerror(errno)); 289} 290 291void 292cleanup0(void) 293{ 294 s = -1; 295} 296 297void 298setup1(void) 299{ 300 fd_set rdfds; 301 struct timeval timeout; 302 int n; 303 304 s = socket(tdat[testno].domain, tdat[testno].type, tdat[testno].proto); 305 if (s < 0) { 306 tst_brkm(TBROK, cleanup, "socket setup failed: %s", 307 strerror(errno)); 308 } 309 if (tdat[testno].type == SOCK_STREAM) { 310 if (tdat[testno].domain == PF_INET) { 311 if (connect(s, (struct sockaddr*)&sin1, sizeof(sin1))<0) 312 tst_brkm(TBROK, cleanup, "connect failed: %s ", 313 strerror(errno)); 314 /* Wait for something to be readable, else we won't detect EFAULT on recv */ 315 FD_ZERO(&rdfds); 316 FD_SET(s, &rdfds); 317 timeout.tv_sec = 2; 318 timeout.tv_usec = 0; 319 n = select(s+1, &rdfds, 0, 0, &timeout); 320 if (n != 1 || !FD_ISSET(s, &rdfds)) 321 tst_brkm(TBROK, cleanup, "client setup1 failed - no message ready in 2 sec"); 322 } else if (tdat[testno].domain == PF_UNIX) { 323 if (connect(s, (struct sockaddr*)&sun1, sizeof(sun1))<0) 324 tst_brkm(TBROK, cleanup, "UD connect failed:", 325 " %s ", strerror(errno)); 326 } 327 } 328} 329 330void 331setup2(void) 332{ 333 setup1(); 334 if (write(s, "R", 1) < 0) { 335 tst_brkm(TBROK, cleanup, "test setup failed: write: %s", 336 strerror(errno)); 337 } 338 control = (struct cmsghdr *)cbuf; 339 controllen = control->cmsg_len = sizeof(cbuf); 340} 341 342void 343setup3(void) 344{ 345 setup2(); 346 controllen = sizeof(struct cmsghdr) -1; 347} 348 349void 350setup4(void) 351{ 352 setup2(); 353 controllen = 128 * 1024; 354} 355 356void 357cleanup1(void) 358{ 359 (void) close(s); 360 s = -1; 361} 362 363void 364cleanup2(void) 365{ 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 377start_server(struct sockaddr_in *ssin, struct sockaddr_un *ssun) 378{ 379 pid_t pid; 380 381 /* set up inet socket */ 382 sfd = socket(PF_INET, SOCK_STREAM, 0); 383 if (sfd < 0) { 384 tst_brkm(TBROK, cleanup, "server socket failed: %s", 385 strerror(errno)); 386 return -1; 387 } 388 if (bind(sfd, (struct sockaddr *)ssin, sizeof(*ssin)) < 0) { 389 tst_brkm(TBROK, cleanup, "server bind failed: %s", 390 strerror(errno)); 391 return -1; 392 } 393 if (listen(sfd, 10) < 0) { 394 tst_brkm(TBROK, cleanup, "server listen failed: %s", 395 strerror(errno)); 396 return -1; 397 } 398 /* set up UNIX-domain socket */ 399 ufd = socket(PF_UNIX, SOCK_STREAM, 0); 400 if (ufd < 0) { 401 tst_brkm(TBROK, cleanup, "server UD socket failed: %s", 402 strerror(errno)); 403 return -1; 404 } 405 if (bind(ufd, (struct sockaddr *)ssun, sizeof(*ssun))) { 406 tst_brkm(TBROK, cleanup, "server UD bind failed: %s", 407 strerror(errno)); 408 return -1; 409 } 410 if (listen(ufd, 10) < 0) { 411 tst_brkm(TBROK, cleanup, "server UD listen failed: %s", 412 strerror(errno)); 413 return -1; 414 } 415 416 switch ((pid = fork())) { 417 case 0: /* child */ 418#ifdef UCLINUX 419 if (self_exec(argv0, "dd", sfd, ufd) < 0) { 420 tst_brkm(TBROK, cleanup, "server self_exec failed"); 421 } 422#else 423 do_child(); 424#endif 425 break; 426 case -1: 427 tst_brkm(TBROK, cleanup, "server fork failed: %s", 428 strerror(errno)); 429 /* fall through */ 430 default: /* parent */ 431 (void) close(sfd); 432 (void) close(ufd); 433 return pid; 434 } 435 /*NOTREACHED*/ 436 exit(1); 437} 438 439void 440do_child() 441{ 442 struct sockaddr_in fsin; 443 struct sockaddr_un fsun; 444 fd_set afds, rfds; 445 int nfds, cc, fd; 446 447 448 FD_ZERO(&afds); 449 FD_SET(sfd, &afds); 450 FD_SET(ufd, &afds); 451 452 nfds = getdtablesize(); 453 454 /* accept connections until killed */ 455 while (1) { 456 int fromlen; 457 458 memcpy(&rfds, &afds, sizeof(rfds)); 459 460 if (select(nfds, &rfds, (fd_set *)0, (fd_set *)0, 461 (struct timeval *)0) < 0) { 462 if (errno != EINTR) { 463 perror("server select"); 464 exit(1); 465 } 466 continue; 467 } 468 if (FD_ISSET(sfd, &rfds)) { 469 int newfd; 470 471 fromlen = sizeof(fsin); 472 newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen); 473 if (newfd >= 0) { 474 FD_SET(newfd, &afds); 475 /* send something back */ 476 (void) write(newfd, "hoser\n", 6); 477 } 478 } 479 if (FD_ISSET(ufd, &rfds)) { 480 int newfd; 481 482 fromlen = sizeof(fsun); 483 newfd = accept(ufd, (struct sockaddr *)&fsun, &fromlen); 484 if (newfd >= 0) 485 FD_SET(newfd, &afds); 486 } 487 for (fd=0; fd<nfds; ++fd) 488 if (fd != sfd && fd != ufd && FD_ISSET(fd, &rfds)) { 489 char rbuf[1024]; 490 491 cc = read(fd, rbuf, sizeof(rbuf)); 492 if (cc && rbuf[0] == 'R') 493 sender(fd); 494 if (cc == 0 || (cc < 0 && errno != EINTR)) { 495 (void) close(fd); 496 FD_CLR(fd, &afds); 497 } 498 } 499 } 500} 501 502#define TM "from recvmsg01 server" 503 504/* special for rights-passing test */ 505void 506sender(int fd) 507{ 508 struct msghdr mh; 509 struct cmsghdr *control; 510 char tmpfn[1024], snd_cbuf[1024]; 511 int tfd; 512 513 (void) strcpy(tmpfn, "smtXXXXXX"); 514 tfd = mkstemp(tmpfn); 515 if (tfd < 0) 516 return; 517 (void) unlink(tmpfn); 518 519 bzero(&mh, sizeof(mh)); 520 521 /* set up cmsghdr */ 522 control = (struct cmsghdr *)snd_cbuf; 523 bzero(control, sizeof(struct cmsghdr)); 524 control->cmsg_len = sizeof(struct cmsghdr)+4; 525 control->cmsg_level = SOL_SOCKET; 526 control->cmsg_type = SCM_RIGHTS; 527 *(int *)CMSG_DATA(control) = tfd; 528 529 /* set up msghdr */ 530 iov[0].iov_base = TM; 531 iov[0].iov_len = sizeof(TM); 532 mh.msg_iov = iov; 533 mh.msg_iovlen = 1; 534 mh.msg_flags = 0; 535 mh.msg_control = control; 536 mh.msg_controllen = control->cmsg_len; 537 538 /* do it */ 539 (void) sendmsg(fd, &mh, 0); 540 (void) close(tfd); 541} 542