Home | History | Annotate | Download | only in sendto
      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