recvmsg01.c revision 865695bbc89088b9526ea9045410e5afb70a985c
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#include <stdio.h> 44#include <unistd.h> 45#include <errno.h> 46 47#include <sys/types.h> 48#include <sys/socket.h> 49#include <sys/signal.h> 50#include <sys/uio.h> 51#include <sys/un.h> 52#include <sys/file.h> 53 54#include <netinet/in.h> 55 56#include "test.h" 57#include "usctest.h" 58 59char *TCID="recvmsg01"; /* Test program identifier. */ 60int testno; 61 62char buf[1024], cbuf[1024]; 63int s; /* socket descriptor */ 64int passed_fd = -1; /* rights-passing test descriptor */ 65struct sockaddr_in sin1, from; 66struct sockaddr_un sun1; 67struct msghdr msgdat; 68struct cmsghdr *control = 0; 69int controllen = 0; 70struct iovec iov[1]; 71 72void setup(void), setup0(void), setup1(void), setup2(void), setup3(void), 73 setup4(void), cleanup(void), cleanup0(void), cleanup1(void), 74 cleanup2(void); 75 76void sender(int); 77char *mktemp(char *); 78 79struct test_case_t { /* test case structure */ 80 int domain; /* PF_INET, PF_UNIX, ... */ 81 int type; /* SOCK_STREAM, SOCK_DGRAM ... */ 82 int proto; /* protocol number (usually 0 = default) */ 83 struct iovec *iov; 84 int iovcnt; 85 void *buf; /* recv data buffer */ 86 int buflen; /* recv buffer length */ 87 struct msghdr *msg; 88 unsigned flags; 89 struct sockaddr *from; /* from address */ 90 int fromlen; /* from address value/result buffer length */ 91 int retval; /* syscall return value */ 92 int experrno; /* expected errno */ 93 void (*setup)(void); 94 void (*cleanup)(void); 95 char *desc; 96} tdat[] = { 97 { PF_INET, SOCK_STREAM, 0, iov, 1, buf, sizeof(buf), &msgdat, 0, 98 (struct sockaddr *)&from, sizeof(from), 99 -1, EBADF, setup0, cleanup0, "bad file descriptor" }, 100 { 0, 0, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 0, 101 (struct sockaddr *)&from, sizeof(from), 102 -1, ENOTSOCK, setup0, cleanup0, "invalid socket" }, 103 { PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 0, 104 (struct sockaddr *)-1, sizeof(from), 105 -1, EFAULT, setup1, cleanup1, "invalid socket buffer" }, 106 { PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 0, 107 (struct sockaddr *)&from, -1, 108 -1, EINVAL, setup1, cleanup1, "invalid socket length" }, 109 { PF_INET, SOCK_STREAM, 0, iov, 1, (void *)-1, sizeof(buf), &msgdat, 0, 110 (struct sockaddr *)&from, sizeof(from), 111 -1, EFAULT, setup1, cleanup1, "invalid recv buffer" }, 112 { PF_INET, SOCK_STREAM, 0, 0, 1, (void *)buf, sizeof(buf), &msgdat, 0, 113 (struct sockaddr *)&from, sizeof(from), 114 -1, EFAULT, setup1, cleanup1, "invalid iovec buffer" }, 115 { PF_INET, SOCK_STREAM, 0, iov, -1, (void *)buf, sizeof(buf), &msgdat,0, 116 (struct sockaddr *)&from, sizeof(from), 117 -1, EINVAL, setup1, cleanup1, "invalid iovec count" }, 118 { PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat,0, 119 (struct sockaddr *)&from, sizeof(from), 120 0, 0, setup2, cleanup2, "rights reception" }, 121 { PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat,-1, 122 (struct sockaddr *)&from, sizeof(from), 123 -1, EINVAL, setup1, cleanup1, "invalid flags set" }, 124 { PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat,0, 125 (struct sockaddr *)&from, sizeof(from), 126 -1, EINVAL, setup3, cleanup2, "invalid cmsg length" }, 127 { PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 128 0, (struct sockaddr *)&from, sizeof(from), 129 0, 0, setup4, cleanup2, "large cmesg length" }, 130}; 131 132int TST_TOTAL=sizeof(tdat)/sizeof(tdat[0]); /* Total number of test cases. */ 133 134int exp_enos[] = {EBADF, ENOTSOCK, EFAULT, EINVAL, 0}; 135 136extern int Tst_count; 137 138int 139main(int argc, char *argv[]) 140{ 141 int lc; /* loop counter */ 142 char *msg; /* message returned from parse_opts */ 143 144 /* Parse standard options given to run the test. */ 145 msg = parse_opts(argc, argv, (option_t *)NULL, NULL); 146 if (msg != (char *)NULL) { 147 tst_brkm(TBROK, tst_exit, "OPTION PARSING ERROR - %s", msg); 148 } 149 150 setup(); 151 152 TEST_EXP_ENOS(exp_enos); 153 154 /* Check looping state if -i option given */ 155 for (lc = 0; TEST_LOOPING(lc); ++lc) { 156 Tst_count = 0; 157 for (testno=0; testno < TST_TOTAL; ++testno) { 158 tdat[testno].setup(); 159 160 /* setup common to all tests */ 161 162 iov[0].iov_base = tdat[testno].buf; 163 iov[0].iov_len = tdat[testno].buflen; 164 msgdat.msg_name = tdat[testno].from; 165 msgdat.msg_namelen = tdat[testno].fromlen; 166 msgdat.msg_iov = tdat[testno].iov; 167 msgdat.msg_iovlen = tdat[testno].iovcnt; 168 msgdat.msg_control = control; 169 msgdat.msg_controllen = controllen; 170 msgdat.msg_flags = 0; 171 172 TEST(recvmsg(s, tdat[testno].msg, tdat[testno].flags)); 173 if (TEST_RETURN >= 0) 174 TEST_RETURN = 0; /* all nonzero equal here */ 175 if (TEST_RETURN != tdat[testno].retval || 176 (TEST_RETURN < 0 && 177 TEST_ERRNO != tdat[testno].experrno)) { 178 tst_resm(TFAIL, "%s ; returned" 179 " %d (expected %d), errno %d (expected" 180 " %d)", tdat[testno].desc, 181 TEST_RETURN, tdat[testno].retval, 182 TEST_ERRNO, tdat[testno].experrno); 183 } else { 184 TEST_ERROR_LOG(TEST_ERRNO); 185 tst_resm(TPASS, "%s successful", 186 tdat[testno].desc); 187 } 188 tdat[testno].cleanup(); 189 } 190 } 191 cleanup(); 192} /* End main */ 193 194pid_t pid; 195char tmpsunpath[1024]; 196 197void 198setup(void) 199{ 200 TEST_PAUSE; /* if -P option specified */ 201 202 /* initialize sockaddr's */ 203 sin1.sin_family = AF_INET; 204 sin1.sin_port = htons((getpid() % 32768) +11000); 205 sin1.sin_addr.s_addr = INADDR_ANY; 206 207 (void) strcpy(tmpsunpath, "/tmp/udsockXXXXXX"); 208 (void) mktemp(tmpsunpath); 209 sun1.sun_family = AF_UNIX; 210 (void) strcpy(sun1.sun_path, tmpsunpath); 211 212 signal(SIGPIPE, SIG_IGN); 213 214 pid = start_server(&sin1, &sun1); 215} 216 217void 218cleanup(void) 219{ 220 (void) kill(pid, SIGKILL); /* kill server */ 221 if (tmpsunpath[0] != '\0') 222 (void) unlink(tmpsunpath); 223 TEST_CLEANUP; 224 tst_exit(); 225} 226 227 228void 229setup0(void) 230{ 231 if (tdat[testno].experrno == EBADF) 232 s = 400; /* anything not an open file */ 233 else 234 s = 0; /* open, but not a socket */ 235} 236 237void 238cleanup0(void) 239{ 240 s = -1; 241} 242 243void 244setup1(void) 245{ 246 s = socket(tdat[testno].domain, tdat[testno].type, tdat[testno].proto); 247 if (s < 0) { 248 tst_brkm(TBROK, cleanup, "socket setup failed: %s", 249 strerror(errno)); 250 } 251 if (tdat[testno].type == SOCK_STREAM) { 252 if (tdat[testno].domain == PF_INET) { 253 if (connect(s, &sin1, sizeof(sin1)) < 0) 254 tst_brkm(TBROK, cleanup, "connect failed: %s ", 255 strerror(errno)); 256 } else if (tdat[testno].domain == PF_UNIX) { 257 if (connect(s, &sun1, sizeof(sun1)) < 0) 258 tst_brkm(TBROK, cleanup, "UD connect failed:", 259 " %s ", strerror(errno)); 260 } 261 } 262} 263 264void 265setup2(void) 266{ 267 setup1(); 268 if (write(s, "R", 1) < 0) { 269 tst_brkm(TBROK, cleanup, "test setup failed: write: %s", 270 strerror(errno)); 271 } 272 control = (struct cmsghdr *)cbuf; 273 controllen = control->cmsg_len = sizeof(cbuf); 274} 275 276void 277setup3(void) 278{ 279 setup2(); 280 controllen = sizeof(struct cmsghdr) -1; 281} 282 283void 284setup4(void) 285{ 286 setup2(); 287 controllen = 128 * 1024; 288} 289 290void 291cleanup1(void) 292{ 293 (void) close(s); 294 s = -1; 295} 296 297void 298cleanup2(void) 299{ 300 (void) close(s); 301 s = -1; 302 303 if (passed_fd >= 0) 304 (void) close(passed_fd); 305 passed_fd = -1; 306 control = 0; 307 controllen = 0; 308} 309 310pid_t 311start_server(struct sockaddr_in *ssin, struct sockaddr_un *ssun) 312{ 313 struct sockaddr_in fsin; 314 struct sockaddr_un fsun; 315 fd_set afds, rfds; 316 pid_t pid; 317 int sfd, nfds, cc, fd, ufd; 318 char c; 319 320 /* set up inet socket */ 321 sfd = socket(PF_INET, SOCK_STREAM, 0); 322 if (sfd < 0) { 323 tst_brkm(TBROK, cleanup, "server socket failed: %s", 324 strerror(errno)); 325 return -1; 326 } 327 if (bind(sfd, (struct sockaddr *)ssin, sizeof(*ssin)) < 0) { 328 tst_brkm(TBROK, cleanup, "server bind failed: %s", 329 strerror(errno)); 330 return -1; 331 } 332 if (listen(sfd, 10) < 0) { 333 tst_brkm(TBROK, cleanup, "server listen failed: %s", 334 strerror(errno)); 335 return -1; 336 } 337 /* set up UNIX-domain socket */ 338 ufd = socket(PF_UNIX, SOCK_STREAM, 0); 339 if (ufd < 0) { 340 tst_brkm(TBROK, cleanup, "server UD socket failed: %s", 341 strerror(errno)); 342 return -1; 343 } 344 if (bind(ufd, (struct sockaddr *)ssun, sizeof(*ssun))) { 345 tst_brkm(TBROK, cleanup, "server UD bind failed: %s", 346 strerror(errno)); 347 return -1; 348 } 349 if (listen(ufd, 10) < 0) { 350 tst_brkm(TBROK, cleanup, "server UD listen failed: %s", 351 strerror(errno)); 352 return -1; 353 } 354 355 switch ((pid = fork())) { 356 case 0: /* child */ 357 break; 358 case -1: 359 tst_brkm(TBROK, cleanup, "server fork failed: %s", 360 strerror(errno)); 361 /* fall through */ 362 default: /* parent */ 363 (void) close(sfd); 364 (void) close(ufd); 365 return pid; 366 } 367 368 FD_ZERO(&afds); 369 FD_SET(sfd, &afds); 370 FD_SET(ufd, &afds); 371 372 nfds = getdtablesize(); 373 374 /* accept connections until killed */ 375 while (1) { 376 int fromlen; 377 378 memcpy(&rfds, &afds, sizeof(rfds)); 379 380 if (select(nfds, &rfds, (fd_set *)0, (fd_set *)0, 381 (struct timeval *)0) < 0) { 382 if (errno != EINTR) { 383 perror("server select"); 384 exit(1); 385 } 386 continue; 387 } 388 if (FD_ISSET(sfd, &rfds)) { 389 int newfd; 390 391 fromlen = sizeof(fsin); 392 newfd = accept(sfd, &fsin, &fromlen); 393 if (newfd >= 0) { 394 FD_SET(newfd, &afds); 395 /* send something back */ 396 (void) write(newfd, "hoser\n", 6); 397 } 398 } 399 if (FD_ISSET(ufd, &rfds)) { 400 int newfd; 401 402 fromlen = sizeof(fsun); 403 newfd = accept(ufd, &fsun, &fromlen); 404 if (newfd >= 0) 405 FD_SET(newfd, &afds); 406 } 407 for (fd=0; fd<nfds; ++fd) 408 if (fd != sfd && fd != ufd && FD_ISSET(fd, &rfds)) { 409 char rbuf[1024]; 410 411 cc = read(fd, rbuf, sizeof(rbuf)); 412 if (cc && rbuf[0] == 'R') 413 sender(fd); 414 if (cc == 0 || (cc < 0 && errno != EINTR)) { 415 (void) close(fd); 416 FD_CLR(fd, &afds); 417 } 418 } 419 } 420} 421 422#define TM "from recvmsg01 server" 423 424/* special for rights-passing test */ 425void 426sender(int fd) 427{ 428 struct msghdr mh; 429 struct cmsghdr *control; 430 char tmpfn[1024], snd_cbuf[1024]; 431 char *p; 432 int tfd; 433 434 (void) strcpy(tmpfn, "/tmp/smtXXXXXX"); 435 p = mktemp(tmpfn); 436 tfd = open(p, O_CREAT|O_RDWR|O_TRUNC, 0600); 437 if (tfd < 0) 438 return; 439 (void) unlink(p); 440 441 bzero(&mh, sizeof(mh)); 442 443 /* set up cmsghdr */ 444 control = (struct cmsghdr *)snd_cbuf; 445 bzero(control, sizeof(struct cmsghdr)); 446 control->cmsg_len = sizeof(struct cmsghdr)+4; 447 control->cmsg_level = SOL_SOCKET; 448 control->cmsg_type = SCM_RIGHTS; 449 *(int *)CMSG_DATA(control) = tfd; 450 451 /* set up msghdr */ 452 iov[0].iov_base = TM; 453 iov[0].iov_len = sizeof(TM); 454 mh.msg_iov = iov; 455 mh.msg_iovlen = 1; 456 mh.msg_flags = 0; 457 mh.msg_control = control; 458 mh.msg_controllen = control->cmsg_len; 459 460 /* do it */ 461 (void) sendmsg(fd, &mh, 0); 462 (void) close(tfd); 463} 464