1 /* 2 * 3 * Copyright (c) International Business Machines Corp., 2001 4 * Copyright (c) Cyril Hrubis <chrubis (at) suse.cz> 2012 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 14 * the GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21 /* 22 * Test Name: sendto01 23 * 24 * Test Description: 25 * Verify that sendto() returns the proper errno for various failure cases 26 * 27 * HISTORY 28 * 07/2001 Ported by Wayne Boyer 29 */ 30 31 #include <stdio.h> 32 #include <unistd.h> 33 #include <errno.h> 34 #include <fcntl.h> 35 36 #include <sys/types.h> 37 #include <sys/socket.h> 38 #include <sys/signal.h> 39 #include <sys/un.h> 40 41 #include <netinet/in.h> 42 43 #include "test.h" 44 #include "safe_macros.h" 45 46 char *TCID = "sendto01"; 47 int testno; 48 49 static char buf[1024], bigbuf[128 * 1024]; 50 static int s; 51 static struct sockaddr_in sin1, sin2; 52 static int sfd; 53 54 struct test_case_t { /* test case structure */ 55 int domain; /* PF_INET, PF_UNIX, ... */ 56 int type; /* SOCK_STREAM, SOCK_DGRAM ... */ 57 int proto; /* protocol number (usually 0 = default) */ 58 void *buf; /* send data buffer */ 59 int buflen; /* send's 3rd argument */ 60 unsigned flags; /* send's 4th argument */ 61 struct sockaddr_in *to; /* destination */ 62 int tolen; /* length of "to" buffer */ 63 int retval; 64 int experrno; 65 void (*setup) (void); 66 void (*cleanup) (void); 67 char *desc; 68 }; 69 70 static void setup(void); 71 static void setup0(void); 72 static void setup1(void); 73 static void setup2(void); 74 static void setup3(void); 75 static void cleanup(void); 76 static void cleanup0(void); 77 static void cleanup1(void); 78 static void do_child(void); 79 80 struct test_case_t tdat[] = { 81 {.domain = PF_INET, 82 .type = SOCK_STREAM, 83 .proto = 0, 84 .buf = buf, 85 .buflen = sizeof(buf), 86 .flags = 0, 87 .to = &sin1, 88 .tolen = sizeof(sin1), 89 .retval = -1, 90 .experrno = EBADF, 91 .setup = setup0, 92 .cleanup = cleanup0, 93 .desc = "bad file descriptor"} 94 , 95 {.domain = 0, 96 .type = 0, 97 .proto = 0, 98 .buf = buf, 99 .buflen = sizeof(buf), 100 .flags = 0, 101 .to = &sin1, 102 .tolen = sizeof(sin1), 103 .retval = -1, 104 .experrno = ENOTSOCK, 105 .setup = setup0, 106 .cleanup = cleanup0, 107 .desc = "invalid socket"} 108 , 109 #ifndef UCLINUX 110 /* Skip since uClinux does not implement memory protection */ 111 {.domain = PF_INET, 112 .type = SOCK_DGRAM, 113 .proto = 0, 114 .buf = (void *)-1, 115 .buflen = sizeof(buf), 116 .flags = 0, 117 .to = &sin1, 118 .tolen = sizeof(sin1), 119 .retval = -1, 120 .experrno = EFAULT, 121 .setup = setup1, 122 .cleanup = cleanup1, 123 .desc = "invalid send buffer"} 124 , 125 #endif 126 {.domain = PF_INET, 127 .type = SOCK_STREAM, 128 .proto = 0, 129 .buf = buf, 130 .buflen = sizeof(buf), 131 .flags = 0, 132 .to = &sin2, 133 .tolen = sizeof(sin2), 134 .retval = 0, 135 .experrno = EFAULT, 136 .setup = setup1, 137 .cleanup = cleanup1, 138 .desc = "connected TCP"} 139 , 140 {.domain = PF_INET, 141 .type = SOCK_STREAM, 142 .proto = 0, 143 .buf = buf, 144 .buflen = sizeof(buf), 145 .flags = 0, 146 .to = &sin1, 147 .tolen = sizeof(sin1), 148 .retval = -1, 149 .experrno = EPIPE, 150 .setup = setup3, 151 .cleanup = cleanup1, 152 .desc = "not connected TCP"} 153 , 154 {.domain = PF_INET, 155 .type = SOCK_DGRAM, 156 .proto = 0, 157 .buf = buf, 158 .buflen = sizeof(buf), 159 .flags = 0, 160 .to = &sin1, 161 .tolen = -1, 162 .retval = -1, 163 .experrno = EINVAL, 164 .setup = setup1, 165 .cleanup = cleanup1, 166 .desc = "invalid to buffer length"} 167 , 168 #ifndef UCLINUX 169 /* Skip since uClinux does not implement memory protection */ 170 {.domain = PF_INET, 171 .type = SOCK_DGRAM, 172 .proto = 0, 173 .buf = buf, 174 .buflen = sizeof(buf), 175 .flags = 0, 176 .to = (struct sockaddr_in *)-1, 177 .tolen = sizeof(sin1), 178 .retval = -1, 179 .experrno = EFAULT, 180 .setup = setup1, 181 .cleanup = cleanup1, 182 .desc = "invalid to buffer"} 183 , 184 #endif 185 {.domain = PF_INET, 186 .type = SOCK_DGRAM, 187 .proto = 0, 188 .buf = bigbuf, 189 .buflen = sizeof(bigbuf), 190 .flags = 0, 191 .to = &sin1, 192 .tolen = sizeof(sin1), 193 .retval = -1, 194 .experrno = EMSGSIZE, 195 .setup = setup1, 196 .cleanup = cleanup1, 197 .desc = "UDP message too big"} 198 , 199 {.domain = PF_INET, 200 .type = SOCK_STREAM, 201 .proto = 0, 202 .buf = buf, 203 .buflen = sizeof(buf), 204 .flags = 0, 205 .to = &sin1, 206 .tolen = sizeof(sin1), 207 .retval = -1, 208 .experrno = EPIPE, 209 .setup = setup2, 210 .cleanup = cleanup1, 211 .desc = "local endpoint shutdown"} 212 , 213 {.domain = PF_INET, 214 .type = SOCK_DGRAM, 215 .proto = 0, 216 .buf = buf, 217 .buflen = sizeof(buf), 218 .flags = MSG_OOB, 219 .to = &sin1, 220 .tolen = sizeof(sin1), 221 .retval = -1, 222 .experrno = EOPNOTSUPP, 223 .setup = setup1, 224 .cleanup = cleanup1, 225 .desc = "invalid flags set"} 226 }; 227 228 int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); 229 230 #ifdef UCLINUX 231 static char *argv0; 232 #endif 233 234 static pid_t start_server(struct sockaddr_in *sin0) 235 { 236 pid_t pid; 237 socklen_t slen = sizeof(*sin0); 238 239 sin0->sin_family = AF_INET; 240 sin0->sin_port = 0; /* pick random free port */ 241 sin0->sin_addr.s_addr = INADDR_ANY; 242 243 sfd = socket(PF_INET, SOCK_STREAM, 0); 244 if (sfd < 0) { 245 tst_brkm(TBROK | TERRNO, cleanup, "server socket failed"); 246 return -1; 247 } 248 if (bind(sfd, (struct sockaddr *)sin0, sizeof(*sin0)) < 0) { 249 tst_brkm(TBROK | TERRNO, cleanup, "server bind failed"); 250 return -1; 251 } 252 if (listen(sfd, 10) < 0) { 253 tst_brkm(TBROK | TERRNO, cleanup, "server listen failed"); 254 return -1; 255 } 256 SAFE_GETSOCKNAME(cleanup, sfd, (struct sockaddr *)sin0, &slen); 257 258 switch ((pid = FORK_OR_VFORK())) { 259 case 0: 260 #ifdef UCLINUX 261 if (self_exec(argv0, "d", sfd) < 0) 262 tst_brkm(TBROK | TERRNO, cleanup, 263 "server self_exec failed"); 264 #else 265 do_child(); 266 #endif 267 break; 268 case -1: 269 tst_brkm(TBROK | TERRNO, cleanup, "server fork failed"); 270 default: 271 (void)close(sfd); 272 return pid; 273 } 274 275 exit(1); 276 } 277 278 static void do_child(void) 279 { 280 struct sockaddr_in fsin; 281 fd_set afds, rfds; 282 int nfds, cc, fd; 283 284 FD_ZERO(&afds); 285 FD_SET(sfd, &afds); 286 287 nfds = sfd + 1; 288 289 /* accept connections until killed */ 290 while (1) { 291 socklen_t fromlen; 292 293 memcpy(&rfds, &afds, sizeof(rfds)); 294 295 if (select(nfds, &rfds, NULL, NULL, NULL) < 0 && errno != EINTR) 296 exit(1); 297 298 if (FD_ISSET(sfd, &rfds)) { 299 int newfd; 300 301 fromlen = sizeof(fsin); 302 newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen); 303 if (newfd >= 0) { 304 FD_SET(newfd, &afds); 305 nfds = MAX(nfds, newfd + 1); 306 } 307 } 308 for (fd = 0; fd < nfds; ++fd) { 309 if (fd != sfd && FD_ISSET(fd, &rfds)) { 310 cc = read(fd, buf, sizeof(buf)); 311 if (cc == 0 || (cc < 0 && errno != EINTR)) { 312 (void)close(fd); 313 FD_CLR(fd, &afds); 314 } 315 } 316 } 317 } 318 } 319 320 int main(int ac, char *av[]) 321 { 322 int lc; 323 324 tst_parse_opts(ac, av, NULL, NULL); 325 326 #ifdef UCLINUX 327 argv0 = av[0]; 328 maybe_run_child(&do_child, "d", &sfd); 329 #endif 330 331 setup(); 332 333 for (lc = 0; TEST_LOOPING(lc); ++lc) { 334 335 tst_count = 0; 336 for (testno = 0; testno < TST_TOTAL; ++testno) { 337 tdat[testno].setup(); 338 339 TEST(sendto(s, tdat[testno].buf, tdat[testno].buflen, 340 tdat[testno].flags, 341 (const struct sockaddr *)tdat[testno].to, 342 tdat[testno].tolen)); 343 344 if (TEST_RETURN > 0) 345 TEST_RETURN = 0; 346 347 if (TEST_RETURN != tdat[testno].retval || 348 (TEST_RETURN < 0 && 349 TEST_ERRNO != tdat[testno].experrno)) { 350 tst_resm(TFAIL, "%s ; returned" 351 " %ld (expected %d), errno %d (expected" 352 " %d)", tdat[testno].desc, 353 TEST_RETURN, tdat[testno].retval, 354 TEST_ERRNO, tdat[testno].experrno); 355 } else { 356 tst_resm(TPASS, "%s successful", 357 tdat[testno].desc); 358 } 359 tdat[testno].cleanup(); 360 } 361 } 362 cleanup(); 363 364 tst_exit(); 365 } 366 367 static pid_t server_pid; 368 369 static void setup(void) 370 { 371 TEST_PAUSE; 372 373 server_pid = start_server(&sin1); 374 375 signal(SIGPIPE, SIG_IGN); 376 } 377 378 static void cleanup(void) 379 { 380 kill(server_pid, SIGKILL); 381 } 382 383 static void setup0(void) 384 { 385 if (tdat[testno].experrno == EBADF) 386 s = 400; 387 else if ((s = open("/dev/null", O_WRONLY)) == -1) 388 tst_brkm(TBROK | TERRNO, cleanup, "open(/dev/null) failed"); 389 } 390 391 static void cleanup0(void) 392 { 393 s = -1; 394 } 395 396 static void setup1(void) 397 { 398 s = SAFE_SOCKET(cleanup, tdat[testno].domain, tdat[testno].type, 399 tdat[testno].proto); 400 SAFE_CONNECT(cleanup, s, (const struct sockaddr *)&sin1, sizeof(sin1)); 401 } 402 403 static void cleanup1(void) 404 { 405 (void)close(s); 406 s = -1; 407 } 408 409 static void setup2(void) 410 { 411 setup1(); 412 if (shutdown(s, 1) < 0) 413 tst_brkm(TBROK | TERRNO, cleanup, "socket setup failed connect " 414 "test %d", testno); 415 } 416 417 static void setup3(void) 418 { 419 s = SAFE_SOCKET(cleanup, tdat[testno].domain, tdat[testno].type, 420 tdat[testno].proto); 421 } 422