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 45 char *TCID = "sendto01"; 46 int testno; 47 48 static char buf[1024], bigbuf[128 * 1024]; 49 static int s; 50 static struct sockaddr_in sin1, sin2; 51 static int sfd; 52 53 struct test_case_t { /* test case structure */ 54 int domain; /* PF_INET, PF_UNIX, ... */ 55 int type; /* SOCK_STREAM, SOCK_DGRAM ... */ 56 int proto; /* protocol number (usually 0 = default) */ 57 void *buf; /* send data buffer */ 58 int buflen; /* send's 3rd argument */ 59 unsigned flags; /* send's 4th argument */ 60 struct sockaddr_in *to; /* destination */ 61 int tolen; /* length of "to" buffer */ 62 int retval; 63 int experrno; 64 void (*setup) (void); 65 void (*cleanup) (void); 66 char *desc; 67 }; 68 69 static void setup(void); 70 static void setup0(void); 71 static void setup1(void); 72 static void setup2(void); 73 static void setup3(void); 74 static void cleanup(void); 75 static void cleanup0(void); 76 static void cleanup1(void); 77 static void do_child(void); 78 79 struct test_case_t tdat[] = { 80 {.domain = PF_INET, 81 .type = SOCK_STREAM, 82 .proto = 0, 83 .buf = buf, 84 .buflen = sizeof(buf), 85 .flags = 0, 86 .to = &sin1, 87 .tolen = sizeof(sin1), 88 .retval = -1, 89 .experrno = EBADF, 90 .setup = setup0, 91 .cleanup = cleanup0, 92 .desc = "bad file descriptor"} 93 , 94 {.domain = 0, 95 .type = 0, 96 .proto = 0, 97 .buf = buf, 98 .buflen = sizeof(buf), 99 .flags = 0, 100 .to = &sin1, 101 .tolen = sizeof(sin1), 102 .retval = -1, 103 .experrno = ENOTSOCK, 104 .setup = setup0, 105 .cleanup = cleanup0, 106 .desc = "invalid socket"} 107 , 108 #ifndef UCLINUX 109 /* Skip since uClinux does not implement memory protection */ 110 {.domain = PF_INET, 111 .type = SOCK_DGRAM, 112 .proto = 0, 113 .buf = (void *)-1, 114 .buflen = sizeof(buf), 115 .flags = 0, 116 .to = &sin1, 117 .tolen = sizeof(sin1), 118 .retval = -1, 119 .experrno = EFAULT, 120 .setup = setup1, 121 .cleanup = cleanup1, 122 .desc = "invalid send buffer"} 123 , 124 #endif 125 {.domain = PF_INET, 126 .type = SOCK_STREAM, 127 .proto = 0, 128 .buf = buf, 129 .buflen = sizeof(buf), 130 .flags = 0, 131 .to = &sin2, 132 .tolen = sizeof(sin2), 133 .retval = 0, 134 .experrno = EFAULT, 135 .setup = setup1, 136 .cleanup = cleanup1, 137 .desc = "connected TCP"} 138 , 139 {.domain = PF_INET, 140 .type = SOCK_STREAM, 141 .proto = 0, 142 .buf = buf, 143 .buflen = sizeof(buf), 144 .flags = 0, 145 .to = &sin1, 146 .tolen = sizeof(sin1), 147 .retval = -1, 148 .experrno = EPIPE, 149 .setup = setup3, 150 .cleanup = cleanup1, 151 .desc = "not connected TCP"} 152 , 153 {.domain = PF_INET, 154 .type = SOCK_DGRAM, 155 .proto = 0, 156 .buf = buf, 157 .buflen = sizeof(buf), 158 .flags = 0, 159 .to = &sin1, 160 .tolen = -1, 161 .retval = -1, 162 .experrno = EINVAL, 163 .setup = setup1, 164 .cleanup = cleanup1, 165 .desc = "invalid to buffer length"} 166 , 167 #ifndef UCLINUX 168 /* Skip since uClinux does not implement memory protection */ 169 {.domain = PF_INET, 170 .type = SOCK_DGRAM, 171 .proto = 0, 172 .buf = buf, 173 .buflen = sizeof(buf), 174 .flags = 0, 175 .to = (struct sockaddr_in *)-1, 176 .tolen = sizeof(sin1), 177 .retval = -1, 178 .experrno = EFAULT, 179 .setup = setup1, 180 .cleanup = cleanup1, 181 .desc = "invalid to buffer"} 182 , 183 #endif 184 {.domain = PF_INET, 185 .type = SOCK_DGRAM, 186 .proto = 0, 187 .buf = bigbuf, 188 .buflen = sizeof(bigbuf), 189 .flags = 0, 190 .to = &sin1, 191 .tolen = sizeof(sin1), 192 .retval = -1, 193 .experrno = EMSGSIZE, 194 .setup = setup1, 195 .cleanup = cleanup1, 196 .desc = "UDP message too big"} 197 , 198 {.domain = PF_INET, 199 .type = SOCK_STREAM, 200 .proto = 0, 201 .buf = buf, 202 .buflen = sizeof(buf), 203 .flags = 0, 204 .to = &sin1, 205 .tolen = sizeof(sin1), 206 .retval = -1, 207 .experrno = EPIPE, 208 .setup = setup2, 209 .cleanup = cleanup1, 210 .desc = "local endpoint shutdown"} 211 , 212 {.domain = PF_INET, 213 .type = SOCK_DGRAM, 214 .proto = 0, 215 .buf = buf, 216 .buflen = sizeof(buf), 217 .flags = MSG_OOB, 218 .to = &sin1, 219 .tolen = sizeof(sin1), 220 .retval = -1, 221 .experrno = EOPNOTSUPP, 222 .setup = setup1, 223 .cleanup = cleanup1, 224 .desc = "invalid flags set"} 225 }; 226 227 int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); 228 229 #ifdef UCLINUX 230 static char *argv0; 231 #endif 232 233 static pid_t start_server(struct sockaddr_in *sin0) 234 { 235 pid_t pid; 236 socklen_t slen = sizeof(*sin0); 237 238 sin0->sin_family = AF_INET; 239 sin0->sin_port = 0; /* pick random free port */ 240 sin0->sin_addr.s_addr = INADDR_ANY; 241 242 sfd = socket(PF_INET, SOCK_STREAM, 0); 243 if (sfd < 0) { 244 tst_brkm(TBROK | TERRNO, cleanup, "server socket failed"); 245 return -1; 246 } 247 if (bind(sfd, (struct sockaddr *)sin0, sizeof(*sin0)) < 0) { 248 tst_brkm(TBROK | TERRNO, cleanup, "server bind failed"); 249 return -1; 250 } 251 if (listen(sfd, 10) < 0) { 252 tst_brkm(TBROK | TERRNO, cleanup, "server listen failed"); 253 return -1; 254 } 255 if (getsockname(sfd, (struct sockaddr *)sin0, &slen) == -1) 256 tst_brkm(TBROK | TERRNO, cleanup, "getsockname failed"); 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 = socket(tdat[testno].domain, tdat[testno].type, tdat[testno].proto); 399 if (s < 0) 400 tst_brkm(TBROK | TERRNO, cleanup, "socket setup failed"); 401 if (connect(s, (const struct sockaddr *)&sin1, sizeof(sin1)) < 0) 402 tst_brkm(TBROK | TERRNO, cleanup, "connect failed"); 403 } 404 405 static void cleanup1(void) 406 { 407 (void)close(s); 408 s = -1; 409 } 410 411 static void setup2(void) 412 { 413 setup1(); 414 if (shutdown(s, 1) < 0) 415 tst_brkm(TBROK | TERRNO, cleanup, "socket setup failed connect " 416 "test %d", testno); 417 } 418 419 static void setup3(void) 420 { 421 s = socket(tdat[testno].domain, tdat[testno].type, tdat[testno].proto); 422 if (s < 0) 423 tst_brkm(TBROK | TERRNO, cleanup, "socket setup failed"); 424 } 425