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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20/* 21 * Test Name: connect01 22 * 23 * Test Description: 24 * Verify that connect() returns the proper errno for various failure cases 25 * 26 * Usage: <for command-line> 27 * connect01 [-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#include <fcntl.h> 47 48#include <sys/types.h> 49#include <sys/socket.h> 50#include <sys/signal.h> 51#include <sys/un.h> 52 53#include <netinet/in.h> 54 55#include "test.h" 56#include "safe_macros.h" 57 58char *TCID = "connect01"; 59int testno; 60 61int s, s2; /* socket descriptor */ 62struct sockaddr_in sin1, sin2, sin3, sin4; 63static int sfd; /* shared between start_server and do_child */ 64 65void setup(void), setup0(void), setup1(void), setup2(void), 66cleanup(void), cleanup0(void), cleanup1(void), do_child(void); 67 68static pid_t start_server(struct sockaddr_in *); 69 70struct test_case_t { /* test case structure */ 71 int domain; /* PF_INET, PF_UNIX, ... */ 72 int type; /* SOCK_STREAM, SOCK_DGRAM ... */ 73 int proto; /* protocol number (usually 0 = default) */ 74 struct sockaddr *sockaddr; /* socket address buffer */ 75 int salen; /* connect's 3rd argument */ 76 int retval; /* syscall return value */ 77 int experrno; /* expected errno */ 78 void (*setup) (void); 79 void (*cleanup) (void); 80 char *desc; 81} tdat[] = { 82 { 83 PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sin1, 84 sizeof(struct sockaddr_in), -1, EBADF, setup0, 85 cleanup0, "bad file descriptor"}, 86#ifndef UCLINUX 87 /* Skip since uClinux does not implement memory protection */ 88 { 89 PF_INET, SOCK_STREAM, 0, (struct sockaddr *)-1, 90 sizeof(struct sockaddr_in), -1, EFAULT, setup1, 91 cleanup1, "invalid socket buffer"}, 92#endif 93 { 94 PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sin1, 95 3, -1, EINVAL, setup1, cleanup1, "invalid salen"}, { 96 0, 0, 0, (struct sockaddr *)&sin1, 97 sizeof(sin1), -1, ENOTSOCK, setup0, cleanup0, 98 "invalid socket"} 99 , { 100 PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sin1, 101 sizeof(sin1), -1, EISCONN, setup2, cleanup1, 102 "already connected"} 103 , { 104 PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sin2, 105 sizeof(sin2), -1, ECONNREFUSED, setup1, cleanup1, 106 "connection refused"} 107 , { 108 PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sin4, 109 sizeof(sin4), -1, EAFNOSUPPORT, setup1, cleanup1, 110 "invalid address family"} 111,}; 112 113int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); 114 115#ifdef UCLINUX 116static char *argv0; 117#endif 118 119int main(int argc, char *argv[]) 120{ 121 int lc; 122 123 tst_parse_opts(argc, argv, NULL, NULL); 124#ifdef UCLINUX 125 argv0 = argv[0]; 126 maybe_run_child(&do_child, "d", &sfd); 127#endif 128 129 setup(); 130 131 for (lc = 0; TEST_LOOPING(lc); ++lc) { 132 tst_count = 0; 133 for (testno = 0; testno < TST_TOTAL; ++testno) { 134 tdat[testno].setup(); 135 136 TEST(connect 137 (s, tdat[testno].sockaddr, tdat[testno].salen)); 138 139 if (TEST_RETURN != tdat[testno].retval || 140 (TEST_RETURN < 0 && 141 TEST_ERRNO != tdat[testno].experrno)) { 142 tst_resm(TFAIL, "%s ; returned" 143 " %ld (expected %d), errno %d (expected" 144 " %d)", tdat[testno].desc, 145 TEST_RETURN, tdat[testno].retval, 146 TEST_ERRNO, tdat[testno].experrno); 147 } else { 148 tst_resm(TPASS, "%s successful", 149 tdat[testno].desc); 150 } 151 tdat[testno].cleanup(); 152 } 153 } 154 cleanup(); 155 156 tst_exit(); 157} 158 159pid_t pid; 160 161void setup(void) 162{ 163 TEST_PAUSE; /* if -p option specified */ 164 165 pid = start_server(&sin1); 166 167 sin2.sin_family = AF_INET; 168 /* this port must be unused! */ 169 sin2.sin_port = tst_get_unused_port(NULL, AF_INET, SOCK_STREAM); 170 sin2.sin_addr.s_addr = INADDR_ANY; 171 172 sin3.sin_family = AF_INET; 173 sin3.sin_port = 0; 174 /* assumes no route to this network! */ 175 sin3.sin_addr.s_addr = htonl(0x0AFFFEFD); 176 177 sin4.sin_family = 47; /* bogus address family */ 178 sin4.sin_port = 0; 179 sin4.sin_addr.s_addr = htonl(0x0AFFFEFD); 180 181} 182 183void cleanup(void) 184{ 185 (void)kill(pid, SIGKILL); 186 187} 188 189void setup0(void) 190{ 191 if (tdat[testno].experrno == EBADF) 192 s = 400; /* anything not an open file */ 193 else if ((s = open("/dev/null", O_WRONLY)) == -1) 194 tst_brkm(TBROK | TERRNO, cleanup, "open(/dev/null) failed"); 195 196} 197 198void cleanup0(void) 199{ 200 close(s); 201 s = -1; 202} 203 204void setup1(void) 205{ 206 s = SAFE_SOCKET(cleanup, tdat[testno].domain, tdat[testno].type, 207 tdat[testno].proto); 208} 209 210void cleanup1(void) 211{ 212 (void)close(s); 213 s = -1; 214} 215 216void setup2(void) 217{ 218 setup1(); /* get a socket in s */ 219 SAFE_CONNECT(cleanup, s, (const struct sockaddr *)&sin1, sizeof(sin1)); 220} 221 222pid_t start_server(struct sockaddr_in *sin0) 223{ 224 pid_t pid; 225 socklen_t slen = sizeof(*sin0); 226 227 sin0->sin_family = AF_INET; 228 sin0->sin_port = 0; /* pick random free port */ 229 sin0->sin_addr.s_addr = INADDR_ANY; 230 231 sfd = socket(PF_INET, SOCK_STREAM, 0); 232 if (sfd < 0) { 233 tst_brkm(TBROK | TERRNO, cleanup, "server socket failed"); 234 return -1; 235 } 236 if (bind(sfd, (struct sockaddr *)sin0, sizeof(*sin0)) < 0) { 237 tst_brkm(TBROK | TERRNO, cleanup, "server bind failed"); 238 return -1; 239 } 240 if (listen(sfd, 10) < 0) { 241 tst_brkm(TBROK | TERRNO, cleanup, "server listen failed"); 242 return -1; 243 } 244 SAFE_GETSOCKNAME(cleanup, sfd, (struct sockaddr *)sin0, &slen); 245 246 switch ((pid = FORK_OR_VFORK())) { 247 case 0: /* child */ 248#ifdef UCLINUX 249 self_exec(argv0, "d", sfd); 250#else 251 do_child(); 252#endif 253 break; 254 case -1: 255 tst_brkm(TBROK | TERRNO, cleanup, "server fork failed"); 256 /* fall through */ 257 default: /* parent */ 258 (void)close(sfd); 259 return pid; 260 } 261 262 return -1; 263} 264 265void do_child(void) 266{ 267 struct sockaddr_in fsin; 268 fd_set afds, rfds; 269 int nfds, cc, fd; 270 char c; 271 272 FD_ZERO(&afds); 273 FD_SET(sfd, &afds); 274 275 nfds = sfd + 1; 276 277 /* accept connections until killed */ 278 while (1) { 279 socklen_t fromlen; 280 281 memcpy(&rfds, &afds, sizeof(rfds)); 282 283 if (select(nfds, &rfds, NULL, NULL, 284 NULL) < 0) 285 if (errno != EINTR) 286 exit(1); 287 if (FD_ISSET(sfd, &rfds)) { 288 int newfd; 289 290 fromlen = sizeof(fsin); 291 newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen); 292 if (newfd >= 0) { 293 FD_SET(newfd, &afds); 294 nfds = MAX(nfds, newfd + 1); 295 } 296 } 297 for (fd = 0; fd < nfds; ++fd) 298 if (fd != sfd && FD_ISSET(fd, &rfds)) { 299 if ((cc = read(fd, &c, 1)) == 0) { 300 (void)close(fd); 301 FD_CLR(fd, &afds); 302 } 303 } 304 } 305} 306