diff --git a/src/tests/kits/net/Jamfile b/src/tests/kits/net/Jamfile index 7a7f70477a..5ef4051e29 100644 --- a/src/tests/kits/net/Jamfile +++ b/src/tests/kits/net/Jamfile @@ -18,5 +18,6 @@ SubInclude HAIKU_TOP src tests kits net DialUpPreflet ; SubInclude HAIKU_TOP src tests kits net multicast ; SubInclude HAIKU_TOP src tests kits net netperf ; SubInclude HAIKU_TOP src tests kits net preflet ; +SubInclude HAIKU_TOP src tests kits net sock ; SubInclude HAIKU_TOP src tests kits net tcp_shell ; SubInclude HAIKU_TOP src tests kits net tcptester ; diff --git a/src/tests/kits/net/sock/Jamfile b/src/tests/kits/net/sock/Jamfile new file mode 100644 index 0000000000..f8f687930c --- /dev/null +++ b/src/tests/kits/net/sock/Jamfile @@ -0,0 +1,11 @@ +SubDir HAIKU_TOP src tests kits net sock ; + +UseHeaders $(SUBDIR) : true ; + +SimpleTest sock : + buffers.c cliopen.c crlf.c error.c looptcp.c + loopudp.c main.c multicast.c pattern.c servopen.c + sleepus.c sockopts.c sourceroute.c sourcetcp.c + sourceudp.c sinktcp.c sinkudp.c tellwait.c write.c writen.c + : $(TARGET_NETWORK_LIBS) +; diff --git a/src/tests/kits/net/sock/addrinfo.h b/src/tests/kits/net/sock/addrinfo.h new file mode 100644 index 0000000000..194c8b2d42 --- /dev/null +++ b/src/tests/kits/net/sock/addrinfo.h @@ -0,0 +1,49 @@ +/* -*- c-basic-offset: 8; -*- */ +#ifndef ADDRINFO_H +#define ADDRINFO_H + +/* + * Everything here really belongs in . + * These defines are separate for now, to avoid having to modify the + * system's header. + */ + +struct addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for host */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ +}; + +/* following for getaddrinfo() */ +#define AI_PASSIVE 1 /* socket is intended for bind() + listen() */ +#define AI_CANONNAME 2 /* return canonical name */ + +/* following for getnameinfo() */ +#define NI_MAXHOST 1025 /* max hostname returned */ +#define NI_MAXSERV 32 /* max service name returned */ + +#define NI_NOFQDN 1 /* do not return FQDN */ +#define NI_NUMERICHOST 2 /* return numeric form of hostname */ +#define NI_NAMEREQD 4 /* return error if hostname not found */ +#define NI_NUMERICSERV 8 /* return numeric form of service name */ +#define NI_DGRAM 16 /* datagram service for getservbyname() */ + +/* error returns */ +#define EAI_ADDRFAMILY 1 /* address family for host not supported */ +#define EAI_AGAIN 2 /* temporary failure in name resolution */ +#define EAI_BADFLAGS 3 /* invalid value for ai_flags */ +#define EAI_FAIL 4 /* non-recoverable fail in name resolution */ +#define EAI_FAMILY 5 /* ai_family not supported */ +#define EAI_MEMORY 6 /* memory allocation failure */ +#define EAI_NODATA 7 /* no address associated with host */ +#define EAI_NONAME 8 /* host nor service provided, or not known */ +#define EAI_SERVICE 9 /* service not supported for ai_socktype */ +#define EAI_SOCKTYPE 10 /* ai_socktype not supported */ +#define EAI_SYSTEM 11 /* system error returned in errno */ + +#endif diff --git a/src/tests/kits/net/sock/buffers.c b/src/tests/kits/net/sock/buffers.c new file mode 100644 index 0000000000..ec56d993af --- /dev/null +++ b/src/tests/kits/net/sock/buffers.c @@ -0,0 +1,60 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include + +void buffers(int sockfd) +{ + int n; + socklen_t optlen; + + /* Allocate the read and write buffers. */ + + if (rbuf == NULL) { + if ( (rbuf = malloc(readlen)) == NULL) + err_sys("malloc error for read buffer"); + } + + if (wbuf == NULL) { + if ( (wbuf = malloc(writelen)) == NULL) + err_sys("malloc error for write buffer"); + } + + /* Set the socket send and receive buffer sizes (if specified). + The receive buffer size is tied to TCP's advertised window. */ + + if (rcvbuflen) { + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuflen, + sizeof(rcvbuflen)) < 0) + err_sys("SO_RCVBUF setsockopt error"); + + optlen = sizeof(n); + if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, &optlen) < 0) + err_sys("SO_RCVBUF getsockopt error"); + if (n != rcvbuflen) + err_quit("error: requested rcvbuflen = %d, resulting SO_RCVBUF = %d", rcvbuflen, n); + if (verbose) + fprintf(stderr, "SO_RCVBUF = %d\n", n); + } + + if (sndbuflen) { + if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sndbuflen, + sizeof(sndbuflen)) < 0) + err_sys("SO_SNDBUF setsockopt error"); + + optlen = sizeof(n); + if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &n, &optlen) < 0) + err_sys("SO_SNDBUF getsockopt error"); + if (n != sndbuflen) + err_quit("error: requested sndbuflen = %d, resulting SO_SNDBUF = %d", sndbuflen, n); + if (verbose) + fprintf(stderr, "SO_SNDBUF = %d\n", n); + } +} diff --git a/src/tests/kits/net/sock/cliopen.c b/src/tests/kits/net/sock/cliopen.c new file mode 100644 index 0000000000..ace24602bc --- /dev/null +++ b/src/tests/kits/net/sock/cliopen.c @@ -0,0 +1,132 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include "sock.h" + +int cliopen(char *host, char *port) +{ + int fd, i, on; + char *protocol; + struct in_addr inaddr; + struct servent *sp; + struct hostent *hp; + + protocol = udp ? "udp" : "tcp"; + + /* initialize socket address structure */ + bzero(&servaddr, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + + /* see if "port" is a service name or number */ + if ( (i = atoi(port)) == 0) { + if ( (sp = getservbyname(port, protocol)) == NULL) + err_quit("getservbyname() error for: %s/%s", port, protocol); + + servaddr.sin_port = sp->s_port; + } else + servaddr.sin_port = htons(i); + + /* + * First try to convert the host name as a dotted-decimal number. + * Only if that fails do we call gethostbyname(). + */ + + if (inet_aton(host, &inaddr) == 1) + servaddr.sin_addr = inaddr; /* it's dotted-decimal */ + else if ( (hp = gethostbyname(host)) != NULL) + bcopy(hp->h_addr, &servaddr.sin_addr, hp->h_length); + else + err_quit("invalid hostname: %s", host); + + if ( (fd = socket(AF_INET, udp ? SOCK_DGRAM : SOCK_STREAM, 0)) < 0) + err_sys("socket() error"); + + if (reuseaddr) { + on = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) + err_sys("setsockopt of SO_REUSEADDR error"); + } + +#ifdef SO_REUSEPORT + if (reuseport) { + on = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof (on)) < 0) + err_sys("setsockopt of SO_REUSEPORT error"); + } +#endif + + /* + * User can specify port number for client to bind. Only real use + * is to see a TCP connection initiated by both ends at the same time. + * Also, if UDP is being used, we specifically call bind() to assign + * an ephemeral port to the socket. + * Also, for experimentation, client can also set local IP address + * (and port) using -l option. Allow localip[] to be set but bindport + * to be 0. + */ + + if (bindport != 0 || localip[0] != 0 || udp) { + bzero(&cliaddr, sizeof(cliaddr)); + cliaddr.sin_family = AF_INET; + cliaddr.sin_port = htons(bindport); /* can be 0 */ + if (localip[0] != 0) { + if (inet_aton(localip, &cliaddr.sin_addr) == 0) + err_quit("invalid IP address: %s", localip); + } else + cliaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* wildcard */ + + if (bind(fd, (struct sockaddr *) &cliaddr, sizeof(cliaddr)) < 0) + err_sys("bind() error"); + } + + /* Need to allocate buffers before connect(), since they can affect + * TCP options (window scale, etc.). + */ + + buffers(fd); + sockopts(fd, 0); /* may also want to set SO_DEBUG */ + + /* + * Connect to the server. Required for TCP, optional for UDP. + */ + + if (udp == 0 || connectudp) { + for ( ; ; ) { + if (connect(fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) + == 0) + break; /* all OK */ + if (errno == EINTR) /* can happen with SIGIO */ + continue; + if (errno == EISCONN) /* can happen with SIGIO */ + break; + err_sys("connect() error"); + } + } + + if (verbose) { + /* Call getsockname() to find local address bound to socket: + TCP ephemeral port was assigned by connect() or bind(); + UDP ephemeral port was assigned by bind(). */ + i = sizeof(cliaddr); + if (getsockname(fd, (struct sockaddr *) &cliaddr, &i) < 0) + err_sys("getsockname() error"); + + /* Can't do one fprintf() since inet_ntoa() stores + the result in a static location. */ + fprintf(stderr, "connected on %s.%d ", + INET_NTOA(cliaddr.sin_addr), ntohs(cliaddr.sin_port)); + fprintf(stderr, "to %s.%d\n", + INET_NTOA(servaddr.sin_addr), ntohs(servaddr.sin_port)); + } + + sockopts(fd, 1); /* some options get set after connect() */ + + return(fd); +} diff --git a/src/tests/kits/net/sock/config.h b/src/tests/kits/net/sock/config.h new file mode 100644 index 0000000000..4cd4574147 --- /dev/null +++ b/src/tests/kits/net/sock/config.h @@ -0,0 +1,186 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ +/* Define the following if you have the corresponding header */ +/* #undef CPU_VENDOR_OS */ +/* #undef HAVE_NETCONFIG_H */ /* */ +/* #undef HAVE_NETDIR_H */ /* */ +/* #undef HAVE_POLL_H */ /* */ +/* #undef HAVE_PTHREAD_H */ /* */ +/* #undef HAVE_STRINGS_H */ /* */ +/* #undef HAVE_SYS_FILIO_H */ /* */ +/* #undef HAVE_SYS_IOCTL_H */ /* */ +/* #undef HAVE_SYS_SELECT_H */ /* */ +/* #undef HAVE_SYS_SOCKIO_H */ /* */ +/* #undef HAVE_SYS_SYSCTL_H */ /* */ +/* #undef HAVE_SYS_TIME_H */ /* */ +/* #undef HAVE_SYS_TYPES_H */ /* */ + +/* Define the following to the appropriate datatype, if necessary */ +/* #undef int8_t */ /* */ +/* #undef int16_t */ /* */ +/* #undef int32_t */ /* */ +/* #undef u_int8_t */ /* */ +/* #undef u_int16_t */ /* */ +/* #undef u_int32_t */ /* */ +/* #undef DEBUG */ + +/* #undef HAVE_ADDRINFO_STRUCT */ /* */ +#define HAVE_MSGHDR_MSG_CONTROL +#define HAVE_ADDRINFO_PROTO 1 + +/* struct addrinfo is defined */ +#define HAVE_ADDRINFO_STRUCT + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +#define HAVE_LIBNSL 1 + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* struct msghdr has msg_control field */ +#define HAVE_MSGHDR_MSG_CONTROL + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETCONFIG_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETDIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_POLL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_PTHREAD_H 1 + +/* Define to 1 if you have the `setlinebuf' function. */ +#define HAVE_SETLINEBUF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STROPTS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_FILIO_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SYSCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Name of package */ +#define PACKAGE "sock" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.3" + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ diff --git a/src/tests/kits/net/sock/crlf.c b/src/tests/kits/net/sock/crlf.c new file mode 100644 index 0000000000..a3a2eb5b04 --- /dev/null +++ b/src/tests/kits/net/sock/crlf.c @@ -0,0 +1,51 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include "sock.h" + +/* Convert newline to return/newline. */ + +int +crlf_add(char *dst, int dstsize, const char *src, int lenin) +{ + int lenout; + char c; + + if ( (lenout = lenin) > dstsize) + err_quit("crlf_add: destination not big enough"); + + for ( ; lenin > 0; lenin--) { + if ( (c = *src++) == '\n') { + if (++lenout >= dstsize) + err_quit("crlf_add: destination not big enough"); + *dst++ = '\r'; + } + *dst++ = c; + } + + return(lenout); +} + +int +crlf_strip(char *dst, int dstsize, const char *src, int lenin) +{ + int lenout; + char c; + + for (lenout = 0; lenin > 0; lenin--) { + if ( (c = *src++) != '\r') { + if (++lenout >= dstsize) + err_quit("crlf_strip: destination not big enough"); + *dst++ = c; + } + } + + return(lenout); +} diff --git a/src/tests/kits/net/sock/error.c b/src/tests/kits/net/sock/error.c new file mode 100644 index 0000000000..9bd3d91f64 --- /dev/null +++ b/src/tests/kits/net/sock/error.c @@ -0,0 +1,104 @@ +/* -*- c-basic-offset: 8; -*- */ +#include /* for definition of errno */ +#include /* ANSI C header file */ +#include "ourhdr.h" + +static void err_doit(int, const char *, va_list); + +char *pname = NULL; /* caller can set this from argv[0] */ + +/* Nonfatal error related to a system call. + * Print a message and return. */ + +void +/* $f err_ret $ */ +err_ret(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(1, fmt, ap); + va_end(ap); + return; +} + +/* Fatal error related to a system call. + * Print a message and terminate. */ + +void +/* $f err_sys $ */ +err_sys(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(1, fmt, ap); + va_end(ap); + exit(1); +} + +/* Fatal error related to a system call. + * Print a message, dump core, and terminate. */ + +void +/* $f err_dump $ */ +err_dump(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(1, fmt, ap); + va_end(ap); + abort(); /* dump core and terminate */ + exit(1); /* shouldn't get here */ +} + +/* Nonfatal error unrelated to a system call. + * Print a message and return. */ + +void +/* $f err_msg $ */ +err_msg(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(0, fmt, ap); + va_end(ap); + return; +} + +/* Fatal error unrelated to a system call. + * Print a message and terminate. */ + +void +/* $f err_quit $ */ +err_quit(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(0, fmt, ap); + va_end(ap); + exit(1); +} + +/* Print a message and return to caller. + * Caller specifies "errnoflag". */ + +static void +err_doit(int errnoflag, const char *fmt, va_list ap) +{ + int errno_save; + char buf[MAXLINE]; + + errno_save = errno; /* value caller might want printed */ + vsprintf(buf, fmt, ap); + if (errnoflag) + sprintf(buf+strlen(buf), ": %s", strerror(errno_save)); + strcat(buf, "\n"); + fflush(stdout); /* in case stdout and stderr are the same */ + fputs(buf, stderr); + fflush(stderr); /* SunOS 4.1.* doesn't grok NULL argument */ + return; +} diff --git a/src/tests/kits/net/sock/global.h b/src/tests/kits/net/sock/global.h new file mode 100644 index 0000000000..1ed2dd4b94 --- /dev/null +++ b/src/tests/kits/net/sock/global.h @@ -0,0 +1,41 @@ +/* -*- c-basic-offset: 8; -*- */ +#ifndef GLOBAL_H +#define GLOBAL_H + +#include "config.h" /* configuration options for current OS */ + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +#include + +#ifndef HAVE_ADDRINFO_STRUCT +#include "addrinfo.h" +#endif + +/* Older resolvers do not have gethostbyname2() */ +#ifndef HAVE_GETHOSTBYNAME2 +#define gethostbyname2(host,family) gethostbyname((host)) +#endif + +/* This avoids a warning with glibc compilation */ +#ifndef errno +extern int errno; +#endif + + +/* Miscellaneous constants */ +#define MAXLINE 4096 /* max text line length */ +#define MAXSOCKADDR 128 /* max socket address structure size */ +#define BUFFSIZE 8192 /* buffer size for reads and writes */ + +/* stdin and stdout file descriptors */ +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 + +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define max(a,b) ((a) > (b) ? (a) : (b)) + + +#endif diff --git a/src/tests/kits/net/sock/looptcp.c b/src/tests/kits/net/sock/looptcp.c new file mode 100644 index 0000000000..72e1f64164 --- /dev/null +++ b/src/tests/kits/net/sock/looptcp.c @@ -0,0 +1,101 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include "sock.h" + +/* Copy everything from stdin to "sockfd", + * and everything from "sockfd" to stdout. */ + +void loop_tcp(int sockfd) +{ + int maxfdp1, nread, ntowrite, stdineof, flags; + fd_set rset; + + if (pauseinit) + sleep_us(pauseinit*1000); /* intended for server */ + + flags = 0; + stdineof = 0; + FD_ZERO(&rset); + maxfdp1 = sockfd + 1; /* check descriptors [0..sockfd] */ + + for ( ; ; ) { + if (stdineof == 0) + FD_SET(STDIN_FILENO, &rset); + FD_SET(sockfd, &rset); + + if (select(maxfdp1, &rset, NULL, NULL, NULL) < 0) + err_sys("select error"); + + if (FD_ISSET(STDIN_FILENO, &rset)) { + /* data to read on stdin */ + if ( (nread = read(STDIN_FILENO, rbuf, readlen)) < 0) + err_sys("read error from stdin"); + else if (nread == 0) { + /* EOF on stdin */ + if (halfclose) { + if (shutdown(sockfd, SHUT_WR) < 0) + err_sys("shutdown() error"); + + FD_CLR(STDIN_FILENO, &rset); + stdineof = 1; /* don't read stdin anymore */ + continue; /* back to select() */ + } + break; /* default: stdin EOF -> done */ + } + + if (crlf) { + ntowrite = crlf_add(wbuf, writelen, rbuf, nread); + if (dowrite(sockfd, wbuf, ntowrite) != ntowrite) + err_sys("write error"); + } else { + if (dowrite(sockfd, rbuf, nread) != nread) + err_sys("write error"); + } + } + + if (FD_ISSET(sockfd, &rset)) { + /* data to read from socket */ + /* msgpeek = 0 or MSG_PEEK */ + flags = msgpeek; + oncemore: + if ( (nread = recv(sockfd, rbuf, readlen, flags)) < 0) + err_sys("recv error"); + else if (nread == 0) { + if (verbose) + fprintf(stderr, "connection closed by peer\n"); + break; /* EOF, terminate */ + } + + if (crlf) { + ntowrite = crlf_strip(wbuf, writelen, rbuf, nread); + if (writen(STDOUT_FILENO, wbuf, ntowrite) != ntowrite) + err_sys("writen error to stdout"); + } else { + if (writen(STDOUT_FILENO, rbuf, nread) != nread) + err_sys("writen error to stdout"); + } + + if (flags != 0) { + flags = 0; /* no infinite loop */ + goto oncemore; /* read the message again */ + } + } + } + + if (pauseclose) { + if (verbose) + fprintf(stderr, "pausing before close\n"); + sleep_us(pauseclose*1000); + } + + if (close(sockfd) < 0) + err_sys("close error"); /* since SO_LINGER may be set */ +} diff --git a/src/tests/kits/net/sock/loopudp.c b/src/tests/kits/net/sock/loopudp.c new file mode 100644 index 0000000000..4a68465e40 --- /dev/null +++ b/src/tests/kits/net/sock/loopudp.c @@ -0,0 +1,217 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include +#include +#include +#include +#include +#include "sock.h" + +/* Copy everything from stdin to "sockfd", + * and everything from "sockfd" to stdout. */ + +void +loop_udp(int sockfd) +{ + int maxfdp1, nread, ntowrite, stdineof, clilen, servlen, flags; + fd_set rset; + struct sockaddr_in cliaddr; /* for UDP server */ + struct sockaddr_in servaddr; /* for UDP client */ + +#ifdef HAVE_MSGHDR_MSG_CONTROL + struct iovec iov[1]; + struct msghdr msg; + +#ifdef IP_RECVDSTADDR /* 4.3BSD Reno and later */ + static struct cmsghdr *cmptr = NULL; /* malloc'ed */ + struct in_addr dstinaddr; /* for UDP server */ +#define CONTROLLEN (sizeof(struct cmsghdr) + sizeof(struct in_addr)) +#endif /* IP_RECVDSTADDR */ + +#endif /* MSG_TRUNC */ + + if (pauseinit) + sleep_us(pauseinit*1000); /* intended for server */ + + flags = 0; + stdineof = 0; + FD_ZERO(&rset); + maxfdp1 = sockfd + 1; /* check descriptors [0..sockfd] */ + + /* If UDP client issues connect(), recv() and write() are used. + Server is harder since cannot issue connect(). We use recvfrom() + or recvmsg(), depending on OS. */ + + for ( ; ; ) { + if (stdineof == 0) + FD_SET(STDIN_FILENO, &rset); + FD_SET(sockfd, &rset); + + if (select(maxfdp1, &rset, NULL, NULL, NULL) < 0) + err_sys("select error"); + + if (FD_ISSET(STDIN_FILENO, &rset)) { + /* data to read on stdin */ + if ( (nread = read(STDIN_FILENO, rbuf, readlen)) < 0) + err_sys("read error from stdin"); + else if (nread == 0) { + /* EOF on stdin */ + if (halfclose) { + if (shutdown(sockfd, SHUT_WR) < 0) + err_sys("shutdown() error"); + + FD_CLR(STDIN_FILENO, &rset); + stdineof = 1; /* don't read stdin anymore */ + continue; /* back to select() */ + } + break; /* default: stdin EOF -> done */ + } + + if (crlf) { + ntowrite = crlf_add(wbuf, writelen, rbuf, nread); + if (connectudp) { + if (write(sockfd, wbuf, ntowrite) != ntowrite) + err_sys("write error"); + } else { + if (sendto(sockfd, wbuf, ntowrite, 0, + (struct sockaddr *) &servaddr, sizeof(servaddr)) + != ntowrite) + err_sys("sendto error"); + } + } else { + if (connectudp) { + if (write(sockfd, rbuf, nread) != nread) + err_sys("write error"); + } else { + if (sendto(sockfd, rbuf, nread, 0, + (struct sockaddr *) &servaddr, sizeof(servaddr)) + != nread) + err_sys("sendto error"); + } + } + } + + if (FD_ISSET(sockfd, &rset)) { + /* data to read from socket */ + if (server) { + clilen = sizeof(cliaddr); +#ifndef MSG_TRUNC /* vanilla BSD sockets */ + nread = recvfrom(sockfd, rbuf, readlen, 0, + (struct sockaddr *) &cliaddr, &clilen); + +#else /* 4.3BSD Reno and later; use recvmsg() to get at MSG_TRUNC flag */ + /* Also lets us get at control information (destination address) */ + + iov[0].iov_base = rbuf; + iov[0].iov_len = readlen; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_name = (caddr_t) &cliaddr; + msg.msg_namelen = clilen; + +#ifdef IP_RECVDSTADDR + if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL) + err_sys("malloc error for control buffer"); + + msg.msg_control = (caddr_t) cmptr; /* for dest address */ + msg.msg_controllen = CONTROLLEN; +#else + msg.msg_control = (caddr_t) 0; /* no ancillary data */ + msg.msg_controllen = 0; +#endif /* IP_RECVDSTADDR */ + msg.msg_flags = 0; /* flags returned here */ + + nread = recvmsg(sockfd, &msg, 0); +#endif /* HAVE_MSGHDR_MSG_CONTROL */ + if (nread < 0) + err_sys("datagram receive error"); + + if (verbose) { + printf("from %s", INET_NTOA(cliaddr.sin_addr)); +#ifdef HAVE_MSGHDR_MSG_CONTROL +#ifdef IP_RECVDSTADDR + if (recvdstaddr) { + if (cmptr->cmsg_len != CONTROLLEN) + err_quit("control length (%d) != %d", + cmptr->cmsg_len, CONTROLLEN); + if (cmptr->cmsg_level != IPPROTO_IP) + err_quit("control level != IPPROTO_IP"); + if (cmptr->cmsg_type != IP_RECVDSTADDR) + err_quit("control type != IP_RECVDSTADDR"); + bcopy(CMSG_DATA(cmptr), &dstinaddr, + sizeof(struct in_addr)); + bzero(cmptr, CONTROLLEN); + + printf(", to %s", INET_NTOA(dstinaddr)); + } +#endif /* IP_RECVDSTADDR */ +#endif /* HAVE_MSGHDR_MSG_CONTROL */ + printf(": "); + fflush(stdout); + } + +#ifdef MSG_TRUNC + if (msg.msg_flags & MSG_TRUNC) + printf("(datagram truncated)\n"); +#endif + + } else if (connectudp) { + /* msgpeek = 0 or MSG_PEEK */ + flags = msgpeek; + oncemore: + if ( (nread = recv(sockfd, rbuf, readlen, flags)) < 0) + err_sys("recv error"); + else if (nread == 0) { + if (verbose) + fprintf(stderr, "connection closed by peer\n"); + break; /* EOF, terminate */ + } + + } else { + /* Must use recvfrom() for unconnected UDP client */ + servlen = sizeof(servaddr); + nread = recvfrom(sockfd, rbuf, readlen, 0, + (struct sockaddr *) &servaddr, &servlen); + if (nread < 0) + err_sys("datagram recvfrom() error"); + + if (verbose) { + printf("from %s", INET_NTOA(servaddr.sin_addr)); + printf(": "); + fflush(stdout); + } + } + + if (crlf) { + ntowrite = crlf_strip(wbuf, writelen, rbuf, nread); + if (writen(STDOUT_FILENO, wbuf, ntowrite) != ntowrite) + err_sys("writen error to stdout"); + } else { + if (writen(STDOUT_FILENO, rbuf, nread) != nread) + err_sys("writen error to stdout"); + } + + if (flags != 0) { + flags = 0; /* no infinite loop */ + goto oncemore; /* read the message again */ + } + } + } + + if (pauseclose) { + if (verbose) + fprintf(stderr, "pausing before close\n"); + sleep_us(pauseclose*1000); + } + + if (close(sockfd) < 0) + err_sys("close error"); /* since SO_LINGER may be set */ +} diff --git a/src/tests/kits/net/sock/main.c b/src/tests/kits/net/sock/main.c new file mode 100644 index 0000000000..73a4414577 --- /dev/null +++ b/src/tests/kits/net/sock/main.c @@ -0,0 +1,447 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif +#include +#include "sock.h" + +char *host; /* hostname or dotted-decimal string */ +char *port; + + /* DefinE global variables */ +int bindport; /* 0 or TCP or UDP port number to bind */ + /* set by -b or -l options */ +int broadcast; /* SO_BROADCAST */ +int cbreak; /* set terminal to cbreak mode */ +int chunkwrite; /* write in small chunks; not all-at-once */ +int client = 1; /* acting as client is the default */ +int connectudp = 1; /* connect UDP client */ +int crlf; /* convert newline to CR/LF & vice versa */ +int debug; /* SO_DEBUG */ +int dofork; /* concurrent server, do a fork() */ +int dontroute; /* SO_DONTROUTE */ +char foreignip[32]; /* foreign IP address, dotted-decimal string */ +int foreignport; /* foreign port number */ +int halfclose; /* TCP half close option */ +int ignorewerr; /* true if write() errors should be ignored */ +int iptos = -1; /* IP_TOS opton */ +int ipttl = -1; /* IP_TTL opton */ +char joinip[32]; /* multicast IP address, dotted-decimal string */ +int keepalive; /* SO_KEEPALIVE */ +long linger = -1; /* 0 or positive turns on option */ +int listenq = 5; /* listen queue for TCP Server */ +char localip[32]; /* local IP address, dotted-decimal string */ +int maxseg; /* TCP_MAXSEG */ +int mcastttl; /* multicast TTL */ +int msgpeek; /* MSG_PEEK */ +int nodelay; /* TCP_NODELAY (Nagle algorithm) */ +int nbuf = 1024; /* number of buffers to write (sink mode) */ +int onesbcast; /* set IP_ONESBCAST for 255.255.255.255 bcasts */ +int pauseclose; /* #ms to sleep after recv FIN, before close */ +int pauseinit; /* #ms to sleep before first read */ +int pauselisten; /* #ms to sleep after listen() */ +int pauserw; /* #ms to sleep before each read or write */ +int reuseaddr; /* SO_REUSEADDR */ +int reuseport; /* SO_REUSEPORT */ +int readlen = 1024; /* default read length for socket */ +int writelen = 1024; /* default write length for socket */ +int recvdstaddr; /* IP_RECVDSTADDR option */ +int rcvbuflen; /* size for SO_RCVBUF */ +int sndbuflen; /* size for SO_SNDBUF */ +long rcvtimeo; /* SO_RCVTIMEO */ +long sndtimeo; /* SO_SNDTIMEO */ +int sroute_cnt; /* count of #IP addresses in route */ +char *rbuf; /* pointer that is malloc'ed */ +char *wbuf; /* pointer that is malloc'ed */ +int server; /* to act as server requires -s option */ +int sigio; /* send SIGIO */ +int sourcesink; /* source/sink mode */ +int udp; /* use UDP instead of TCP */ +int urgwrite; /* write urgent byte after this write */ +int verbose; /* each -v increments this by 1 */ +int usewritev; /* use writev() instead of write() */ + +struct sockaddr_in cliaddr, servaddr; + +static void usage(const char *); + +int +main(int argc, char *argv[]) +{ + int c, fd; + char *ptr; + + if (argc < 2) + usage(""); + + opterr = 0; /* don't want getopt() writing to stderr */ + while ( (c = getopt(argc, argv, "2b:cf:g:hij:kl:n:op:q:r:st:uvw:x:y:ABCDEFG:H:IJ:KL:NO:P:Q:R:S:TU:VWX:YZ")) != -1) { + switch (c) { +#ifdef IP_ONESBCAST + case '2': /* use 255.255.255.255 as broadcast address */ + onesbcast = 1; + break; +#endif + + case 'b': + bindport = atoi(optarg); + break; + + case 'c': /* convert newline to CR/LF & vice versa */ + crlf = 1; + break; + + case 'f': /* foreign IP address and port#: a.b.c.d.p */ + if ( (ptr = strrchr(optarg, '.')) == NULL) + usage("invalid -f option"); + + *ptr++ = 0; /* null replaces final period */ + foreignport = atoi(ptr); /* port number */ + strcpy(foreignip, optarg); /* save dotted-decimal IP */ + break; + + case 'g': /* loose source route */ + sroute_doopt(0, optarg); + break; + + case 'h': /* TCP half-close option */ + halfclose = 1; + break; + + case 'i': /* source/sink option */ + sourcesink = 1; + break; + +#ifdef IP_ADD_MEMBERSHIP + case 'j': /* join multicast group a.b.c.d */ + strcpy(joinip, optarg); /* save dotted-decimal IP */ + break; +#endif + + case 'k': /* chunk-write option */ + chunkwrite = 1; + break; + + case 'l': /* local IP address and port#: a.b.c.d.p */ + if ( (ptr = strrchr(optarg, '.')) == NULL) + usage("invalid -l option"); + + *ptr++ = 0; /* null replaces final period */ + bindport = atoi(ptr); /* port number */ + strcpy(localip, optarg); /* save dotted-decimal IP */ + break; + + case 'n': /* number of buffers to write */ + nbuf = atol(optarg); + break; + + case 'o': /* do not connect UDP client */ + connectudp = 0; + break; + + case 'p': /* pause before each read or write */ + pauserw = atoi(optarg); + break; + + case 'q': /* listen queue for TCP server */ + listenq = atoi(optarg); + break; + + case 'r': /* read() length */ + readlen = atoi(optarg); + break; + + case 's': /* server */ + server = 1; + client = 0; + break; + +#ifdef IP_MULTICAST_TTL + case 't': /* IP_MULTICAST_TTL */ + mcastttl = atoi(optarg); + break; +#endif + + case 'u': /* use UDP instead of TCP */ + udp = 1; + break; + + case 'v': /* output what's going on */ + verbose++; + break; + + case 'w': /* write() length */ + writelen = atoi(optarg); + break; + + case 'x': /* SO_RCVTIMEO socket option */ + rcvtimeo = atol(optarg); + break; + + case 'y': /* SO_SNDTIMEO socket option */ + sndtimeo = atol(optarg); + break; + + case 'A': /* SO_REUSEADDR socket option */ + reuseaddr = 1; + break; + + case 'B': /* SO_BROADCAST socket option */ + broadcast = 1; + break; + + case 'C': /* set standard input to cbreak mode */ + cbreak = 1; + break; + + case 'D': /* SO_DEBUG socket option */ + debug = 1; + break; + + case 'E': /* IP_RECVDSTADDR socket option */ + recvdstaddr = 1; + break; + + case 'F': /* concurrent server, do a fork() */ + dofork = 1; + break; + + case 'G': /* strict source route */ + sroute_doopt(1, optarg); + break; + +#ifdef IP_TOS + case 'H': /* IP_TOS socket option */ + iptos = atoi(optarg); + break; +#endif + + case 'I': /* SIGIO signal */ + sigio = 1; + break; + +#ifdef IP_TTL + case 'J': /* IP_TTL socket option */ + ipttl = atoi(optarg); + break; +#endif + + case 'K': /* SO_KEEPALIVE socket option */ + keepalive = 1; + break; + + case 'L': /* SO_LINGER socket option */ + linger = atol(optarg); + break; + + case 'N': /* SO_NODELAY socket option */ + nodelay = 1; + break; + + case 'O': /* pause before listen(), before first accept() */ + pauselisten = atoi(optarg); + break; + + case 'P': /* pause before first read() */ + pauseinit = atoi(optarg); + break; + + case 'Q': /* pause after receiving FIN, but before close() */ + pauseclose = atoi(optarg); + break; + + case 'R': /* SO_RCVBUF socket option */ + rcvbuflen = atoi(optarg); + break; + + case 'S': /* SO_SNDBUF socket option */ + sndbuflen = atoi(optarg); + break; + +#ifdef SO_REUSEPORT + case 'T': /* SO_REUSEPORT socket option */ + reuseport = 1; + break; +#endif + + case 'U': /* when to write urgent byte */ + urgwrite = atoi(optarg); + break; + + case 'V': /* use writev() instead of write() */ + usewritev = 1; + chunkwrite = 1; /* implies this option too */ + break; + + case 'W': /* ignore write errors */ + ignorewerr = 1; + break; + + case 'X': /* TCP maximum segment size option */ + maxseg = atoi(optarg); + break; + + case 'Y': /* SO_DONTROUTE socket option */ + dontroute = 1; + break; + + case 'Z': /* MSG_PEEK option */ + msgpeek = MSG_PEEK; + break; + + case '?': + usage("unrecognized option"); + } + } + + /* check for options that don't make sense */ + if (udp && halfclose) + usage("can't specify -h and -u"); + if (udp && debug) + usage("can't specify -D and -u"); + if (udp && linger >= 0) + usage("can't specify -L and -u"); + if (udp && nodelay) + usage("can't specify -N and -u"); +#ifdef notdef + if (udp == 0 && broadcast) + usage("can't specify -B with TCP"); +#endif + if (udp == 0 && foreignip[0] != 0) + usage("can't specify -f with TCP"); + + if (client) { + if (optind != argc-2) + usage("missing and/or "); + host = argv[optind]; + port = argv[optind+1]; + + } else { + /* If server specifies host and port, then local address is + bound to the "host" argument, instead of being wildcarded. */ + if (optind == argc-2) { + host = argv[optind]; + port = argv[optind+1]; + } else if (optind == argc-1) { + host = NULL; + port = argv[optind]; + } else + usage("missing "); + } + + if (client) + fd = cliopen(host, port); + else + fd = servopen(host, port); + + if (sourcesink) { /* ignore stdin/stdout */ + if (client) { + if (udp) + source_udp(fd); + else + source_tcp(fd); + } else { + if (udp) + sink_udp(fd); + else + sink_tcp(fd); + } + + } else { /* copy stdin/stdout to/from socket */ + if (udp) + loop_udp(fd); + else + loop_tcp(fd); + } + + exit(0); +} + +static void +usage(const char *msg) +{ + err_msg( +"usage: sock [ options ] (for client; default)\n" +" sock [ options ] -s [ ] (for server)\n" +" sock [ options ] -i (for \"source\" client)\n" +" sock [ options ] -i -s [ ] (for \"sink\" server)\n" +"options: -b n bind n as client's local port number\n" +" -c convert newline to CR/LF & vice versa\n" +" -f a.b.c.d.p foreign IP address = a.b.c.d, foreign port# = p\n" +" -g a.b.c.d loose source route\n" +" -h issue TCP half close on standard input EOF\n" +" -i \"source\" data to socket, \"sink\" data from socket (w/-s)\n" +#ifdef IP_ADD_MEMBERSHIP +" -j a.b.c.d join multicast group\n" +#endif +" -k write or writev in chunks\n" +" -l a.b.c.d.p client's local IP address = a.b.c.d, local port# = p\n" +" -n n #buffers to write for \"source\" client (default 1024)\n" +" -o do NOT connect UDP client\n" +" -p n #ms to pause before each read or write (source/sink)\n" +" -q n size of listen queue for TCP server (default 5)\n" +" -r n #bytes per read() for \"sink\" server (default 1024)\n" +" -s operate as server instead of client\n" +#ifdef IP_MULTICAST_TTL +" -t n set multicast ttl\n" +#endif +" -u use UDP instead of TCP\n" +" -v verbose\n" +" -w n #bytes per write() for \"source\" client (default 1024)\n" +" -x n #ms for SO_RCVTIMEO (receive timeout)\n" +" -y n #ms for SO_SNDTIMEO (send timeout)\n" +" -A SO_REUSEADDR option\n" +" -B SO_BROADCAST option\n" +" -C set terminal to cbreak mode\n" +" -D SO_DEBUG option\n" +" -E IP_RECVDSTADDR option\n" +" -F fork after connection accepted (TCP concurrent server)\n" +" -G a.b.c.d strict source route\n" +#ifdef IP_TOS +" -H n IP_TOS option (16=min del, 8=max thru, 4=max rel, 2=min$)\n" +#endif +" -I SIGIO signal\n" +#ifdef IP_TTL +" -J n IP_TTL option\n" +#endif +" -K SO_KEEPALIVE option\n" +" -L n SO_LINGER option, n = linger time\n" +" -N TCP_NODELAY option\n" +" -O n #ms to pause after listen, but before first accept\n" +" -P n #ms to pause before first read or write (source/sink)\n" +" -Q n #ms to pause after receiving FIN, but before close\n" +" -R n SO_RCVBUF option\n" +" -S n SO_SNDBUF option\n" +#ifdef SO_REUSEPORT +" -T SO_REUSEPORT option\n" +#endif +" -U n enter urgent mode before write number n (source only)\n" +" -V use writev() instead of write(); enables -k too\n" +" -W ignore write errors for sink client\n" +" -X n TCP_MAXSEG option (set MSS)\n" +" -Y SO_DONTROUTE option\n" +" -Z MSG_PEEK\n" +#ifdef IP_ONESBCAST +" -2 IP_ONESBCAST option (255.255.255.255 for broadcast\n" +#endif +); + + if (msg[0] != 0) + err_quit("%s", msg); + exit(1); +} diff --git a/src/tests/kits/net/sock/multicast.c b/src/tests/kits/net/sock/multicast.c new file mode 100644 index 0000000000..0d2d14be40 --- /dev/null +++ b/src/tests/kits/net/sock/multicast.c @@ -0,0 +1,33 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include "sock.h" + +void +join_mcast(int fd, struct sockaddr_in *sin) +{ +#ifdef IP_ADD_MEMBERSHIP /* only include if host supports mcasting */ + u_long inaddr; + struct ip_mreq mreq; + + inaddr = sin->sin_addr.s_addr; + if (IN_MULTICAST(inaddr) == 0) + return; /* not a multicast address */ + + mreq.imr_multiaddr.s_addr = inaddr; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); /* need way to change */ + if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) == -1 ) + err_sys("IP_ADD_MEMBERSHIP error"); + + if (verbose) + fprintf(stderr, "multicast group joined\n"); +#endif /* IP_ADD_MEMBERSHIP */ +} diff --git a/src/tests/kits/net/sock/ourhdr.h b/src/tests/kits/net/sock/ourhdr.h new file mode 100644 index 0000000000..fdd45be4f5 --- /dev/null +++ b/src/tests/kits/net/sock/ourhdr.h @@ -0,0 +1,122 @@ +/* -*- c-basic-offset: 8; -*- */ +/* Our own header, to be included *after* all standard system headers */ + +#ifndef __ourhdr_h +#define __ourhdr_h + +#include /* required for some of our prototypes */ +#include /* for convenience */ +#include /* for convenience */ +#include /* for convenience */ +#include /* for convenience */ + +#ifdef notdef /* delete for systems that don't define this (SunOS 4.x) */ +typedef int ssize_t; +#endif + +#ifdef notdef /* delete if doesn't define these for getopt() */ +extern char *optarg; +extern int optind, opterr, optopt; +#endif + +#ifdef notdef /* delete if send() not supported (DEC OSF/1) */ +#define send(a,b,c,d) sendto((a), (b), (c), (d), (struct sockaddr *) NULL, 0) +#endif + +#define MAXLINE 4096 /* max line length */ + +#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) + /* default file access permissions for new files */ +#define DIR_MODE (FILE_MODE | S_IXUSR | S_IXGRP | S_IXOTH) + /* default permissions for new directories */ + +typedef void Sigfunc(int); /* for signal handlers */ + + /* 4.3BSD Reno doesn't define SIG_ERR */ +#if defined(SIG_IGN) && !defined(SIG_ERR) +#define SIG_ERR ((Sigfunc *)-1) +#endif + +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define max(a,b) ((a) > (b) ? (a) : (b)) + + /* prototypes for our own functions */ +char *path_alloc(int *); /* {Prog pathalloc} */ +int open_max(void); /* {Prog openmax} */ +void clr_fl(int, int); /* {Prog setfl} */ +void set_fl(int, int); /* {Prog setfl} */ +void pr_exit(int); /* {Prog prexit} */ +void pr_mask(const char *); /* {Prog prmask} */ +Sigfunc *signal_intr(int, Sigfunc *);/* {Prog signal_intr_function} */ + +int tty_cbreak(int); /* {Prog raw} */ +int tty_raw(int); /* {Prog raw} */ +int tty_reset(int); /* {Prog raw} */ +void tty_atexit(void); /* {Prog raw} */ +#ifdef ECHO /* only if has been included */ +struct termios *tty_termios(void); /* {Prog raw} */ +#endif + +void sleep_us(unsigned int); /* {Ex sleepus} */ +ssize_t readn(int, void *, size_t);/* {Prog readn} */ +ssize_t writen(int, const void *, size_t);/* {Prog writen} */ +int daemon_init(void); /* {Prog daemoninit} */ + +int s_pipe(int *); /* {Progs svr4_spipe bsd_spipe} */ +int recv_fd(int, ssize_t (*func)(int, const void *, size_t)); + /* {Progs recvfd_svr4 recvfd_43bsd} */ +int send_fd(int, int); /* {Progs sendfd_svr4 sendfd_43bsd} */ +int send_err(int, int, const char *);/* {Prog senderr} */ +int serv_listen(const char *); /* {Progs servlisten_svr4 servlisten_44bsd} */ +int serv_accept(int, uid_t *); /* {Progs servaccept_svr4 servaccept_44bsd} */ +int cli_conn(const char *); /* {Progs cliconn_svr4 cliconn_44bsd} */ +int buf_args(char *, int (*func)(int, char **)); + /* {Prog bufargs} */ + +int ptym_open(char *); /* {Progs ptyopen_svr4 ptyopen_44bsd} */ +int ptys_open(int, char *); /* {Progs ptyopen_svr4 ptyopen_44bsd} */ +#ifdef TIOCGWINSZ +pid_t pty_fork(int *, char *, const struct termios *, + const struct winsize *); /* {Prog ptyfork} */ +#endif + +int lock_reg(int, int, int, off_t, int, off_t); + /* {Prog lockreg} */ +#define read_lock(fd, offset, whence, len) \ + lock_reg(fd, F_SETLK, F_RDLCK, offset, whence, len) +#define readw_lock(fd, offset, whence, len) \ + lock_reg(fd, F_SETLKW, F_RDLCK, offset, whence, len) +#define write_lock(fd, offset, whence, len) \ + lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, len) +#define writew_lock(fd, offset, whence, len) \ + lock_reg(fd, F_SETLKW, F_WRLCK, offset, whence, len) +#define un_lock(fd, offset, whence, len) \ + lock_reg(fd, F_SETLK, F_UNLCK, offset, whence, len) + +pid_t lock_test(int, int, off_t, int, off_t); + /* {Prog locktest} */ + +#define is_readlock(fd, offset, whence, len) \ + lock_test(fd, F_RDLCK, offset, whence, len) +#define is_writelock(fd, offset, whence, len) \ + lock_test(fd, F_WRLCK, offset, whence, len) + +void err_dump(const char *, ...); /* {App misc_source} */ +void err_msg(const char *, ...); +void err_quit(const char *, ...); +void err_ret(const char *, ...); +void err_sys(const char *, ...); + +void log_msg(const char *, ...); /* {App misc_source} */ +void log_open(const char *, int, int); +void log_quit(const char *, ...); +void log_ret(const char *, ...); +void log_sys(const char *, ...); + +void TELL_WAIT(void); /* parent/child from {Sec race_conditions} */ +void TELL_PARENT(pid_t); +void TELL_CHILD(pid_t); +void WAIT_PARENT(void); +void WAIT_CHILD(void); + +#endif /* __ourhdr_h */ diff --git a/src/tests/kits/net/sock/pattern.c b/src/tests/kits/net/sock/pattern.c new file mode 100644 index 0000000000..614306b105 --- /dev/null +++ b/src/tests/kits/net/sock/pattern.c @@ -0,0 +1,25 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include "sock.h" +#include + +void +pattern(char *ptr, int len) +{ + char c; + + c = 0; + while(len-- > 0) { + while(isprint((c & 0x7F)) == 0) + c++; /* skip over nonprinting characters */ + *ptr++ = (c++ & 0x7F); + } +} diff --git a/src/tests/kits/net/sock/servopen.c b/src/tests/kits/net/sock/servopen.c new file mode 100644 index 0000000000..7b61d7ea4f --- /dev/null +++ b/src/tests/kits/net/sock/servopen.c @@ -0,0 +1,154 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + + +#include +#include +#include +#include +#include +#include "sock.h" + +int +servopen(char *host, char *port) +{ + int fd, newfd, i, on, pid; + char *protocol; + struct in_addr inaddr; + struct servent *sp; + + protocol = udp ? "udp" : "tcp"; + + /* Initialize the socket address structure */ + bzero(&servaddr, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + + /* Caller normally wildcards the local Internet address, meaning + a connection will be accepted on any connected interface. + We only allow an IP address for the "host", not a name. */ + if (host == NULL) + servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* wildcard */ + else { + if (inet_aton(host, &inaddr) == 0) + err_quit("invalid host name for server: %s", host); + servaddr.sin_addr = inaddr; + } + + /* See if "port" is a service name or number */ + if ( (i = atoi(port)) == 0) { + if ( (sp = getservbyname(port, protocol)) == NULL) + err_ret("getservbyname() error for: %s/%s", port, protocol); + + servaddr.sin_port = sp->s_port; + } else + servaddr.sin_port = htons(i); + + if ( (fd = socket(AF_INET, udp ? SOCK_DGRAM : SOCK_STREAM, 0)) < 0) + err_sys("socket() error"); + + if (reuseaddr) { + on = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) + err_sys("setsockopt of SO_REUSEADDR error"); + } + +#ifdef SO_REUSEPORT + if (reuseport) { + on = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) + err_sys("setsockopt of SO_REUSEPORT error"); + } +#endif + + /* Bind our well-known port so the client can connect to us. */ + if (bind(fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) + err_sys("can't bind local address"); + + join_mcast(fd, &servaddr); + + if (udp) { + buffers(fd); + + if (foreignip[0] != 0) { /* connect to foreignip/port# */ + bzero(&cliaddr, sizeof(cliaddr)); + if (inet_aton(foreignip, &cliaddr.sin_addr) == 0) + err_quit("invalid IP address: %s", foreignip); + cliaddr.sin_family = AF_INET; + cliaddr.sin_port = htons(foreignport); + /* connect() for datagram socket doesn't appear to allow + wildcarding of either IP address or port number */ + + if (connect(fd, (struct sockaddr *) &cliaddr, sizeof(cliaddr)) + < 0) + err_sys("connect() error"); + + } + + sockopts(fd, 1); + + return(fd); /* nothing else to do */ + } + + buffers(fd); /* may set receive buffer size; must do here to get + correct window advertised on SYN */ + sockopts(fd, 0); /* only set some socket options for fd */ + + listen(fd, listenq); + + if (pauselisten) + sleep_us(pauselisten*1000); /* lets connection queue build up */ + + if (dofork) + TELL_WAIT(); /* initialize synchronization primitives */ + + for ( ; ; ) { + i = sizeof(cliaddr); + if ( (newfd = accept(fd, (struct sockaddr *) &cliaddr, &i)) < 0) + err_sys("accept() error"); + + if (dofork) { + if ( (pid = fork()) < 0) + err_sys("fork error"); + + if (pid > 0) { + close(newfd); /* parent closes connected socket */ + WAIT_CHILD(); /* wait for child to output to terminal */ + continue; /* and back to for(;;) for another accept() */ + } else { + close(fd); /* child closes listening socket */ + } + } + + /* child (or iterative server) continues here */ + if (verbose) { + /* Call getsockname() to find local address bound to socket: + local internet address is now determined (if multihomed). */ + i = sizeof(servaddr); + if (getsockname(newfd, (struct sockaddr *) &servaddr, &i) < 0) + err_sys("getsockname() error"); + + /* Can't do one fprintf() since inet_ntoa() stores + the result in a static location. */ + fprintf(stderr, "connection on %s.%d ", + INET_NTOA(servaddr.sin_addr), ntohs(servaddr.sin_port)); + fprintf(stderr, "from %s.%d\n", + INET_NTOA(cliaddr.sin_addr), ntohs(cliaddr.sin_port)); + } + + buffers(newfd); /* setsockopt() again, in case it didn't propagate + from listening socket to connected socket */ + sockopts(newfd, 1); /* can set all socket options for this socket */ + + if (dofork) + TELL_PARENT(getppid()); /* tell parent we're done with terminal */ + + return(newfd); + } +} diff --git a/src/tests/kits/net/sock/sinktcp.c b/src/tests/kits/net/sock/sinktcp.c new file mode 100644 index 0000000000..957da48d0f --- /dev/null +++ b/src/tests/kits/net/sock/sinktcp.c @@ -0,0 +1,62 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include +#include "sock.h" + +void +sink_tcp(int sockfd) +{ + int n, flags; + + if (pauseinit) + sleep_us(pauseinit*1000); + + for ( ; ; ) { /* read until peer closes connection; -n opt ignored */ + /* msgpeek = 0 or MSG_PEEK */ + flags = msgpeek; + oncemore: + if ( (n = recv(sockfd, rbuf, readlen, flags)) < 0) { + err_sys("recv error"); + + } else if (n == 0) { + if (verbose) + fprintf(stderr, "connection closed by peer\n"); + break; + +#ifdef notdef /* following not possible with TCP */ + } else if (n != readlen) + err_quit("read returned %d, expected %d", n, readlen); +#else + } +#endif + + if (verbose) + fprintf(stderr, "received %d bytes%s\n", n, + (flags == MSG_PEEK) ? " (MSG_PEEK)" : ""); + + if (pauserw) + sleep_us(pauserw*1000); + + if (flags != 0) { + flags = 0; /* no infinite loop */ + goto oncemore; /* read the message again */ + } + } + + if (pauseclose) { /* pausing here puts peer into FIN_WAIT_2 */ + if (verbose) + fprintf(stderr, "pausing before close\n"); + sleep_us(pauseclose*1000); + } + + if (close(sockfd) < 0) + err_sys("close error"); /* since SO_LINGER may be set */ +} diff --git a/src/tests/kits/net/sock/sinkudp.c b/src/tests/kits/net/sock/sinkudp.c new file mode 100644 index 0000000000..3a45060f8f --- /dev/null +++ b/src/tests/kits/net/sock/sinkudp.c @@ -0,0 +1,70 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include +#include "sock.h" + +void +sink_udp(int sockfd) /* TODO: use recvfrom ?? */ +{ + int n, flags; + + if (pauseinit) + sleep_us(pauseinit*1000); + + for ( ; ; ) { /* read until peer closes connection; -n opt ignored */ + /* msgpeek = 0 or MSG_PEEK */ + flags = msgpeek; + oncemore: + if ( (n = recv(sockfd, rbuf, readlen, flags)) < 0) { + err_sys("recv error"); + + } else if (n == 0) { + if (verbose) + fprintf(stderr, "connection closed by peer\n"); + break; + +#ifdef notdef /* following not possible with TCP */ + } else if (n != readlen) + err_quit("read returned %d, expected %d", n, readlen); +#else + } +#endif + + if (verbose) { + fprintf(stderr, "received %d bytes%s\n", n, + (flags == MSG_PEEK) ? " (MSG_PEEK)" : ""); + if (verbose > 1) { + fprintf(stderr, "printing %d bytes\n", n); + rbuf[n] = 0; /* make certain it's null terminated */ + fprintf(stderr, "SDAP header: %lx\n", *((long *) rbuf)); + fprintf(stderr, "next long: %lx\n", *((long *) rbuf+4)); + fputs(&rbuf[8], stderr); + } + } + + if (pauserw) + sleep_us(pauserw*1000); + + if (flags != 0) { + flags = 0; /* avoid infinite loop */ + goto oncemore; /* read the message again */ + } +} + +if (pauseclose) { + if (verbose) + fprintf(stderr, "pausing before close\n"); + sleep_us(pauseclose*1000); + } + +if (close(sockfd) < 0) + err_sys("close error"); +} diff --git a/src/tests/kits/net/sock/sleepus.c b/src/tests/kits/net/sock/sleepus.c new file mode 100644 index 0000000000..47ed1cf12e --- /dev/null +++ b/src/tests/kits/net/sock/sleepus.c @@ -0,0 +1,30 @@ +/* -*- c-basic-offset: 8; -*- */ +#include +#include +#include +#include +#include "ourhdr.h" + +void +sleep_us(unsigned int nusecs) +{ + struct timeval tval; + + for ( ; ; ) { + tval.tv_sec = nusecs / 1000000; + tval.tv_usec = nusecs % 1000000; + if (select(0, NULL, NULL, NULL, &tval) == 0) + break; /* all OK */ + /* + * Note than on an interrupted system call (i.e, SIGIO) there's not + * much we can do, since the timeval{} isn't updated with the time + * remaining. We could obtain the clock time before the call, and + * then obtain the clock time here, subtracting them to determine + * how long select() blocked before it was interrupted, but that + * seems like too much work :-) + */ + if (errno == EINTR) + continue; + err_sys("sleep_us: select error"); + } +} diff --git a/src/tests/kits/net/sock/sock.h b/src/tests/kits/net/sock/sock.h new file mode 100644 index 0000000000..c001c58dfc --- /dev/null +++ b/src/tests/kits/net/sock/sock.h @@ -0,0 +1,163 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + + +#include "config.h" /* configuration options for current OS */ + +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +#include +#include + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#ifndef HAVE_ADDRINFO_STRUCT +#include "addrinfo.h" +#endif + +#include +#ifdef __bsdi__ +#include /* required before tcp.h, for BYTE_ORDER */ +#endif +#include /* TCP_NODELAY */ +#include /* getservbyname(), gethostbyname() */ +#include +#include +#include +#include +#include + + + +/* Older resolvers do not have gethostbyname2() */ +#ifndef HAVE_GETHOSTBYNAME2 +#define gethostbyname2(host,family) gethostbyname((host)) +#endif + +/* This avoids a warning with glibc compilation */ +#ifndef errno +extern int errno; +#endif + + +/* Miscellaneous constants */ +#define MAXLINE 4096 /* max text line length */ +#define MAXSOCKADDR 128 /* max socket address structure size */ +#define BUFFSIZE 8192 /* buffer size for reads and writes */ + +/* stdin and stdout file descriptors */ +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 + +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define max(a,b) ((a) > (b) ? (a) : (b)) + +/* declare global variables */ +extern int bindport; +extern int broadcast; +extern int cbreak; +extern int chunkwrite; +extern int client; +extern int connectudp; +extern int crlf; +extern int debug; +extern int dofork; +extern int dontroute; +extern char foreignip[]; +extern int foreignport; +extern int halfclose; +extern int ignorewerr; +extern int iptos; +extern int ipttl; +extern char joinip[]; +extern int keepalive; +extern long linger; +extern int listenq; +extern char localip[]; +extern int maxseg; +extern int mcastttl; +extern int msgpeek; +extern int nodelay; +extern int nbuf; +extern int onesbcast; +extern int pauseclose; +extern int pauseinit; +extern int pauselisten; +extern int pauserw; +extern int reuseaddr; +extern int reuseport; +extern int readlen; +extern int writelen; +extern int recvdstaddr; +extern int rcvbuflen; +extern int sndbuflen; +extern long rcvtimeo; +extern long sndtimeo; +extern char *rbuf; +extern char *wbuf; +extern int server; +extern int sigio; +extern int sourcesink; +extern int sroute_cnt; +extern int udp; +extern int urgwrite; +extern int verbose; +extern int usewritev; + +extern struct sockaddr_in cliaddr, servaddr; + +/* Earlier versions of gcc under SunOS 4.x have problems passing arguments + that are structs (as opposed to pointers to structs). This shows up + with inet_ntoa, whose argument is a "struct in_addr". */ + +#if defined(sun) && defined(__GNUC__) && defined(GCC_STRUCT_PROBLEM) +#define INET_NTOA(foo) inet_ntoa(&foo) +#else +#define INET_NTOA(foo) inet_ntoa(foo) +#endif + + /* function prototypes */ +void buffers(int); +int cliopen(char *, char *); +int crlf_add(char *, int, const char *, int); +int crlf_strip(char *, int, const char *, int); +void join_mcast(int, struct sockaddr_in *); +void loop_tcp(int); +void loop_udp(int); +void pattern(char *, int); +int servopen(char *, char *); +void sink_tcp(int); +void sink_udp(int); +void source_tcp(int); +void source_udp(int); +void sroute_doopt(int, char *); +void sroute_set(int); +void sleep_us(unsigned int); +void sockopts(int, int); +ssize_t dowrite(int, const void *, size_t); + +void TELL_WAIT(void); +void TELL_PARENT(pid_t); +void WAIT_PARENT(void); +void TELL_CHILD(pid_t); +void WAIT_CHILD(void); + +void err_dump(const char *, ...); +void err_msg(const char *, ...); +void err_quit(const char *, ...); +void err_ret(const char *, ...); +void err_sys(const char *, ...); + +ssize_t writen(int, const void *, size_t); diff --git a/src/tests/kits/net/sock/sockopts.c b/src/tests/kits/net/sock/sockopts.c new file mode 100644 index 0000000000..7574a54a87 --- /dev/null +++ b/src/tests/kits/net/sock/sockopts.c @@ -0,0 +1,374 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sock.h" +#include +#include + +#ifdef FIOASYNC +static void sigio_func(int); + +static void +sigio_func(int signo) +{ + fprintf(stderr, "SIGIO\n"); + /* shouldn't printf from a signal handler ... */ +} +#endif + +void +sockopts(int sockfd, int doall) +{ + int option; + unsigned optlen; + struct linger ling; + struct timeval timer; + + /* "doall" is 0 for a server's listening socket (i.e., before + accept() has returned.) Some socket options such as SO_KEEPALIVE + don't make sense at this point, while others like SO_DEBUG do. */ + + if (debug) { + option = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_DEBUG, + &option, sizeof(option)) < 0) + err_sys("SO_DEBUG setsockopt error"); + + option = 0; + optlen = sizeof(option); + if (getsockopt(sockfd, SOL_SOCKET, SO_DEBUG, + &option, &optlen) < 0) + err_sys("SO_DEBUG getsockopt error"); + if (option == 0) + err_quit("SO_DEBUG not set (%d)", option); + + if (verbose) + fprintf(stderr, "SO_DEBUG set\n"); + } + + if (dontroute) { + option = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_DONTROUTE, + &option, sizeof(option)) < 0) + err_sys("SO_DONTROUTE setsockopt error"); + + option = 0; + optlen = sizeof(option); + if (getsockopt(sockfd, SOL_SOCKET, SO_DONTROUTE, + &option, &optlen) < 0) + err_sys("SO_DONTROUTE getsockopt error"); + if (option == 0) + err_quit("SO_DONTROUTE not set (%d)", option); + + if (verbose) + fprintf(stderr, "SO_DONTROUTE set\n"); + } + +#ifdef IP_TOS + if (iptos != -1 && doall == 0) { + if (setsockopt(sockfd, IPPROTO_IP, IP_TOS, + &iptos, sizeof(iptos)) < 0) + err_sys("IP_TOS setsockopt error"); + + option = 0; + optlen = sizeof(option); + if (getsockopt(sockfd, IPPROTO_IP, IP_TOS, + &option, &optlen) < 0) + err_sys("IP_TOS getsockopt error"); + if (option != iptos) + err_quit("IP_TOS not set (%d)", option); + + if (verbose) + fprintf(stderr, "IP_TOS set to %d\n", iptos); + } +#endif + +#ifdef IP_TTL + if (ipttl != -1 && doall == 0) { + if (setsockopt(sockfd, IPPROTO_IP, IP_TTL, + &ipttl, sizeof(ipttl)) < 0) + err_sys("IP_TTL setsockopt error"); + + option = 0; + optlen = sizeof(option); + if (getsockopt(sockfd, IPPROTO_IP, IP_TTL, + &option, &optlen) < 0) + err_sys("IP_TTL getsockopt error"); + if (option != ipttl) + err_quit("IP_TTL not set (%d)", option); + + if (verbose) + fprintf(stderr, "IP_TTL set to %d\n", ipttl); + } +#endif + + if (maxseg && udp == 0) { + /* Need to set MSS for server before connection established */ + /* Beware: some kernels do not let the process set this socket + option; others only let it be decreased. */ + if (setsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, + &maxseg, sizeof(maxseg)) < 0) + err_sys("TCP_MAXSEG setsockopt error"); + + option = 0; + optlen = sizeof(option); + if (getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, + &option, &optlen) < 0) + err_sys("TCP_MAXSEG getsockopt error"); + + if (verbose) + fprintf(stderr, "TCP_MAXSEG = %d\n", option); + } + + if (sroute_cnt > 0) + sroute_set(sockfd); + + if (broadcast) { + option = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, + &option, sizeof(option)) < 0) + err_sys("SO_BROADCAST setsockopt error"); + + option = 0; + optlen = sizeof(option); + if (getsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, + &option, &optlen) < 0) + err_sys("SO_BROADCAST getsockopt error"); + if (option == 0) + err_quit("SO_BROADCAST not set (%d)", option); + + if (verbose) + fprintf(stderr, "SO_BROADCAST set\n"); + +#ifdef IP_ONESBCAST + if (onesbcast) { + option = 1; + if (setsockopt(sockfd, IPPROTO_IP, IP_ONESBCAST, + &option, sizeof(option)) < 0) + err_sys("IP_ONESBCAST setsockopt error"); + + option = 0; + optlen = sizeof(option); + if (getsockopt(sockfd, IPPROTO_IP, IP_ONESBCAST, + &option, &optlen) < 0) + err_sys("IP_ONESBCAST getsockopt error"); + if (option == 0) + err_quit("IP_ONESBCAST not set (%d)", option); + + if (verbose) + fprintf(stderr, "IP_ONESBCAST set\n"); + } +#endif + } + +#ifdef IP_ADD_MEMBERSHIP + if (joinip[0]) { + struct ip_mreq join; + + if (inet_aton(joinip, &join.imr_multiaddr) == 0) + err_quit("invalid multicast address: %s", joinip); + join.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &join, sizeof(join)) < 0) + err_sys("IP_ADD_MEMBERSHIP setsockopt error"); + + if (verbose) + fprintf(stderr, "IP_ADD_MEMBERSHIP set\n"); + } +#endif + +#ifdef IP_MULTICAST_TTL + if (mcastttl) { + u_char ttl = mcastttl; + + if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, + &ttl, sizeof(ttl)) < 0) + err_sys("IP_MULTICAST_TTL setsockopt error"); + + optlen = sizeof(ttl); + if (getsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, + &ttl, &optlen) < 0) + err_sys("IP_MULTICAST_TTL getsockopt error"); + if (ttl != mcastttl) + err_quit("IP_MULTICAST_TTL not set (%d)", ttl); + + if (verbose) + fprintf(stderr, "IP_MULTICAST_TTL set to %d\n", ttl); + } +#endif + + if (keepalive && doall && udp == 0) { + option = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, + &option, sizeof(option)) < 0) + err_sys("SO_KEEPALIVE setsockopt error"); + + option = 0; + optlen = sizeof(option); + if (getsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, + &option, &optlen) < 0) + err_sys("SO_KEEPALIVE getsockopt error"); + if (option == 0) + err_quit("SO_KEEPALIVE not set (%d)", option); + + if (verbose) + fprintf(stderr, "SO_KEEPALIVE set\n"); + } + + if (nodelay && doall && udp == 0) { + option = 1; + if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, + &option, sizeof(option)) < 0) + err_sys("TCP_NODELAY setsockopt error"); + + option = 0; + optlen = sizeof(option); + if (getsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, + &option, &optlen) < 0) + err_sys("TCP_NODELAY getsockopt error"); + if (option == 0) + err_quit("TCP_NODELAY not set (%d)", option); + + if (verbose) + fprintf(stderr, "TCP_NODELAY set\n"); + } + + if (doall && verbose && udp == 0) { /* just print MSS if verbose */ + option = 0; + optlen = sizeof(option); + if (getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, + &option, &optlen) < 0) + err_sys("TCP_MAXSEG getsockopt error"); + + fprintf(stderr, "TCP_MAXSEG = %d\n", option); + } + + if (linger >= 0 && doall && udp == 0) { + ling.l_onoff = 1; + ling.l_linger = linger; /* 0 for abortive disconnect */ + if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, + &ling, sizeof(ling)) < 0) + err_sys("SO_LINGER setsockopt error"); + + ling.l_onoff = 0; + ling.l_linger = -1; + optlen = sizeof(struct linger); + if (getsockopt(sockfd, SOL_SOCKET, SO_LINGER, + &ling, &optlen) < 0) + err_sys("SO_LINGER getsockopt error"); + if (ling.l_onoff == 0 || ling.l_linger != linger) + err_quit("SO_LINGER not set (%d, %d)", ling.l_onoff, ling.l_linger); + + if (verbose) + fprintf(stderr, "linger %s, time = %d\n", + ling.l_onoff ? "on" : "off", ling.l_linger); + } + + if (doall && rcvtimeo) { +#ifdef SO_RCVTIMEO + /* User specifies millisec, must convert to sec/usec */ + timer.tv_sec = rcvtimeo / 1000; + timer.tv_usec = (rcvtimeo % 1000) * 1000; + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, + &timer, sizeof(timer)) < 0) + err_sys("SO_RCVTIMEO setsockopt error"); + + timer.tv_sec = timer.tv_usec = 0; + optlen = sizeof(timer); + if (getsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, + &timer, &optlen) < 0) + err_sys("SO_RCVTIMEO getsockopt error"); + + if (verbose) + fprintf(stderr, "SO_RCVTIMEO: %ld.%06ld\n", + timer.tv_sec, timer.tv_usec); +#else + fprintf(stderr, "warning: SO_RCVTIMEO not supported by host\n"); +#endif + } + + if (doall && sndtimeo) { +#ifdef SO_SNDTIMEO + /* User specifies millisec, must convert to sec/usec */ + timer.tv_sec = sndtimeo / 1000; + timer.tv_usec = (sndtimeo % 1000) * 1000; + if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, + &timer, sizeof(timer)) < 0) + err_sys("SO_SNDTIMEO setsockopt error"); + + timer.tv_sec = timer.tv_usec = 0; + optlen = sizeof(timer); + if (getsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, + &timer, &optlen) < 0) + err_sys("SO_SNDTIMEO getsockopt error"); + + if (verbose) + fprintf(stderr, "SO_SNDTIMEO: %ld.%06ld\n", + timer.tv_sec, timer.tv_usec); +#else + fprintf(stderr, "warning: SO_SNDTIMEO not supported by host\n"); +#endif + } + + if (recvdstaddr && udp) { +#ifdef IP_RECVDSTADDR + option = 1; + if (setsockopt(sockfd, IPPROTO_IP, IP_RECVDSTADDR, + &option, sizeof(option)) < 0) + err_sys("IP_RECVDSTADDR setsockopt error"); + + option = 0; + optlen = sizeof(option); + if (getsockopt(sockfd, IPPROTO_IP, IP_RECVDSTADDR, + &option, &optlen) < 0) + err_sys("IP_RECVDSTADDR getsockopt error"); + if (option == 0) + err_quit("IP_RECVDSTADDR not set (%d)", option); + + if (verbose) + fprintf(stderr, "IP_RECVDSTADDR set\n"); +#else + fprintf(stderr, "warning: IP_RECVDSTADDR not supported by host\n"); +#endif + } + + if (sigio) { +#ifdef FIOASYNC + /* + * Should be able to set this with fcntl(O_ASYNC) or fcntl(FASYNC), + * but some systems (AIX?) only do it with ioctl(). + * + * Need to set this for listening socket and for connected socket. + */ + signal(SIGIO, sigio_func); + + if (fcntl(sockfd, F_SETOWN, getpid()) < 0) + err_sys("fcntl F_SETOWN error"); + + option = 1; + if (ioctl(sockfd, FIOASYNC, (char *) &option) < 0) + err_sys("ioctl FIOASYNC error"); + + if (verbose) + fprintf(stderr, "FIOASYNC set\n"); +#else + fprintf(stderr, "warning: FIOASYNC not supported by host\n"); +#endif + } +} diff --git a/src/tests/kits/net/sock/sourceroute.c b/src/tests/kits/net/sock/sourceroute.c new file mode 100644 index 0000000000..7a4fd2de2d --- /dev/null +++ b/src/tests/kits/net/sock/sourceroute.c @@ -0,0 +1,114 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include +#include "sock.h" +#include +#include +#include +#include + +/* + * There is a fundamental limit of 9 IP addresses in a source route. + * But we allocate sroute_opt[44] with room for 10 IP addresses (and + * the 3-byte source route type/len/offset) because with the BSD + * IP_OPTIONS socket option we can specify up to 9 addresses, followed + * by the destination address. The in_pcbopts() function in the kernel + * then takes the final address in the list (the destination) and moves + * it to the front, as shown in Figure 9.33 of "TCP/IP Illustrated, + * Volume 2". Also note that this destination address that we pass as + * the final IP address in our array overrides the destination address + * of the sendto() (Figure 9.28 of Volume 2). + */ + +u_char sroute_opt[44]; /* some implementations require this to be + on a 4-byte boundary */ +u_char *optr; /* pointer into options being formed */ + +/* + * Process either the -g (loose) or -G (strict) command-line option, + * specifying one hop in a source route. + * Either option can be specified up to 9 times. + * With IPv4 the entire source route is either loose or strict, but we + * set the source route type field based on the first option encountered, + * either -g or -G. + */ + +void +sroute_doopt(int strict, char *argptr) +{ + struct in_addr inaddr; + struct hostent *hp; + + if (sroute_cnt >= 9) + err_quit("too many source routes with: %s", argptr); + + if (sroute_cnt == 0) { /* first one */ + bzero(sroute_opt, sizeof(sroute_opt)); + optr = sroute_opt; + *optr++ = strict ? IPOPT_SSRR : IPOPT_LSRR; + optr++; /* we fill in the total length later */ + *optr++ = 4; /* ptr to first source-route address */ + } + + if (inet_aton(argptr, &inaddr) == 1) { + bcopy(&inaddr, optr, sizeof(u_long)); /* dotted decimal */ + if (verbose) + fprintf(stderr, "source route to %s\n", inet_ntoa(inaddr)); + } else if ( (hp = gethostbyname(argptr)) != NULL) { + bcopy(hp->h_addr, optr, sizeof(u_long));/* hostname */ + if (verbose) + fprintf(stderr, "source route to %s\n", + inet_ntoa(*((struct in_addr *) hp->h_addr))); + } else + err_quit("unknown host: %s\n", argptr); + + optr += sizeof(u_long); /* for next IP addr in list */ + sroute_cnt++; +} + +/* + * Set the actual source route with the IP_OPTIONS socket option. + * This function is called if srouce_cnt is nonzero. + * The final destination goes at the end of the list of IP addresses. + */ + +void +sroute_set(int sockfd) +{ + sroute_cnt++; /* account for destination */ + sroute_opt[1] = 3 + (sroute_cnt * 4); /* total length, incl. destination */ + + /* destination must be stored as final entry */ + bcopy(&servaddr.sin_addr, optr, sizeof(u_long)); + optr += sizeof(u_long); + if (verbose) { + fprintf(stderr, "source route to %s\n", inet_ntoa(servaddr.sin_addr)); + fprintf(stderr, "source route size %d bytes\n", sroute_opt[1]); + } + + /* + * The number of bytes that we pass to setsockopt() must be a multiple + * of 4. Since the buffer was initialized to 0, this leaves an EOL + * following the final IP address. + * For optimization we could put a NOP before the 3-byte type/len/offset + * field, which would then align all the IP addresses on 4-byte boundaries, + * but the source routing code is not exactly in the fast path of most + * routers. + */ + while ((optr - sroute_opt) & 3) + optr++; + + if (setsockopt(sockfd, IPPROTO_IP, IP_OPTIONS, + sroute_opt, optr - sroute_opt) < 0) + err_sys("setsockopt error for IP_OPTIONS"); + + sroute_cnt = 0; /* don't call this function again */ +} diff --git a/src/tests/kits/net/sock/sourcetcp.c b/src/tests/kits/net/sock/sourcetcp.c new file mode 100644 index 0000000000..c9a93c6013 --- /dev/null +++ b/src/tests/kits/net/sock/sourcetcp.c @@ -0,0 +1,62 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include +#include "sock.h" + +void +source_tcp(int sockfd) +{ + int i, n, option; + socklen_t optlen; + char oob; + + pattern(wbuf, writelen); /* fill send buffer with a pattern */ + + if (pauseinit) + sleep_us(pauseinit*1000); + + for (i = 1; i <= nbuf; i++) { + if (urgwrite == i) { + oob = urgwrite; + if ( (n = send(sockfd, &oob, 1, MSG_OOB)) != 1) + err_sys("send of MSG_OOB returned %d, expected %d", + n, writelen); + if (verbose) + fprintf(stderr, "wrote %d byte of urgent data\n", n); + } + + if ( (n = write(sockfd, wbuf, writelen)) != writelen) { + if (ignorewerr) { + err_ret("write returned %d, expected %d", n, writelen); + /* also call getsockopt() to clear so_error */ + optlen = sizeof(option); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, + &option, &optlen) < 0) + err_sys("SO_ERROR getsockopt error"); + } else + err_sys("write returned %d, expected %d", n, writelen); + + } else if (verbose) + fprintf(stderr, "wrote %d bytes\n", n); + + if (pauserw) + sleep_us(pauserw*1000); + } + + if (pauseclose) { + if (verbose) + fprintf(stderr, "pausing before close\n"); + sleep_us(pauseclose*1000); + } + + if (close(sockfd) < 0) + err_sys("close error"); /* since SO_LINGER may be set */ +} diff --git a/src/tests/kits/net/sock/sourceudp.c b/src/tests/kits/net/sock/sourceudp.c new file mode 100644 index 0000000000..08c1e14398 --- /dev/null +++ b/src/tests/kits/net/sock/sourceudp.c @@ -0,0 +1,72 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ +#include +#include + + +#include +#include "sock.h" + +void +source_udp(int sockfd) /* TODO: use sendto ?? */ +{ + int i, n, option; + socklen_t optlen; + + pattern(wbuf, writelen); /* fill send buffer with a pattern */ + + if (pauseinit) + sleep_us(pauseinit*1000); + + for (i = 1; i <= nbuf; i++) { + if (connectudp) { + if ( (n = write(sockfd, wbuf, writelen)) != writelen) { + if (ignorewerr) { + err_ret("write returned %d, expected %d", n, writelen); + /* also call getsockopt() to clear so_error */ + optlen = sizeof(option); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, + &option, &optlen) < 0) + err_sys("SO_ERROR getsockopt error"); + } else + err_sys("write returned %d, expected %d", n, writelen); + } + } else { + if ( (n = sendto(sockfd, wbuf, writelen, 0, + (struct sockaddr *) &servaddr, + sizeof(struct sockaddr))) != writelen) { + if (ignorewerr) { + err_ret("sendto returned %d, expected %d", n, writelen); + /* also call getsockopt() to clear so_error */ + optlen = sizeof(option); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, + &option, &optlen) < 0) + err_sys("SO_ERROR getsockopt error"); + } else + err_sys("sendto returned %d, expected %d", n, writelen); + } + } + + if (verbose) + fprintf(stderr, "wrote %d bytes\n", n); + + if (pauserw) + sleep_us(pauserw*1000); + } + + if (pauseclose) { + if (verbose) + fprintf(stderr, "pausing before close\n"); + sleep_us(pauseclose*1000); + } + + if (close(sockfd) < 0) + err_sys("close error"); /* since SO_LINGER may be set */ +} diff --git a/src/tests/kits/net/sock/tellwait.c b/src/tests/kits/net/sock/tellwait.c new file mode 100644 index 0000000000..4422de669c --- /dev/null +++ b/src/tests/kits/net/sock/tellwait.c @@ -0,0 +1,68 @@ +/* -*- c-basic-offset: 8; -*- */ +#include +#include "ourhdr.h" + +static volatile sig_atomic_t sigflag; + /* set nonzero by signal handler */ +static sigset_t newmask, oldmask, zeromask; + +static void +sig_usr(int signo) /* one signal handler for SIGUSR1 and SIGUSR2 */ +{ + sigflag = 1; + return; +} + +void +TELL_WAIT() +{ + if (signal(SIGUSR1, sig_usr) == SIG_ERR) + err_sys("signal(SIGINT) error"); + if (signal(SIGUSR2, sig_usr) == SIG_ERR) + err_sys("signal(SIGQUIT) error"); + + sigemptyset(&zeromask); + + sigemptyset(&newmask); + sigaddset(&newmask, SIGUSR1); + sigaddset(&newmask, SIGUSR2); + /* block SIGUSR1 and SIGUSR2, and save current signal mask */ + if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) + err_sys("SIG_BLOCK error"); +} + +void +TELL_PARENT(pid_t pid) +{ + kill(pid, SIGUSR2); /* tell parent we're done */ +} + +void +WAIT_PARENT(void) +{ + while (sigflag == 0) + sigsuspend(&zeromask); /* and wait for parent */ + + sigflag = 0; + /* reset signal mask to original value */ + if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) + err_sys("SIG_SETMASK error"); +} + +void +TELL_CHILD(pid_t pid) +{ + kill(pid, SIGUSR1); /* tell child we're done */ +} + +void +WAIT_CHILD(void) +{ + while (sigflag == 0) + sigsuspend(&zeromask); /* and wait for child */ + + sigflag = 0; + /* reset signal mask to original value */ + if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) + err_sys("SIG_SETMASK error"); +} diff --git a/src/tests/kits/net/sock/write.c b/src/tests/kits/net/sock/write.c new file mode 100644 index 0000000000..b48192e3c5 --- /dev/null +++ b/src/tests/kits/net/sock/write.c @@ -0,0 +1,67 @@ +/* -*- c-basic-offset: 8; -*- + * + * Copyright (c) 1993 W. Richard Stevens. All rights reserved. + * Permission to use or modify this software and its documentation only for + * educational purposes and without fee is hereby granted, provided that + * the above copyright notice appear in all copies. The author makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + */ + +#include "sock.h" + +#ifndef UIO_MAXIOV +#define UIO_MAXIOV 16 /* we assume this; may not be true? */ +#endif + +ssize_t +dowrite(int fd, const void *vptr, size_t nbytes) +{ + struct iovec iov[UIO_MAXIOV]; + const char *ptr; + int chunksize, i, n, nleft, nwritten, ntotal; + + if (chunkwrite == 0 && usewritev == 0) + return(write(fd, vptr, nbytes)); /* common case */ + + /* + * Figure out what sized chunks to write. + * Try to use UIO_MAXIOV chunks. + */ + + chunksize = nbytes / UIO_MAXIOV; + if (chunksize <= 0) + chunksize = 1; + else if ((nbytes % UIO_MAXIOV) != 0) + chunksize++; + + ptr = vptr; + nleft = nbytes; + for (i = 0; i < UIO_MAXIOV; i++) + { + iov[i].iov_base = (void *) ptr; + n = (nleft >= chunksize) ? chunksize : nleft; + iov[i].iov_len = n; + if (verbose) + fprintf(stderr, "iov[%2d].iov_base = %x, iov[%2d].iov_len = %d\n", + i, (u_int32_t) iov[i].iov_base, i, (int) iov[i].iov_len); + ptr += n; + if ((nleft -= n) == 0) + break; + } + if (i == UIO_MAXIOV) + err_quit("i == UIO_MAXIOV"); + + if (usewritev) + return(writev(fd, iov, i+1)); + else { + ntotal = 0; + for (n = 0; n <= i; n++) { + nwritten = write(fd, iov[n].iov_base, iov[n].iov_len); + if (nwritten != (int) iov[n].iov_len) + return(-1); + ntotal += nwritten; + } + return(ntotal); + } +} diff --git a/src/tests/kits/net/sock/writen.c b/src/tests/kits/net/sock/writen.c new file mode 100644 index 0000000000..1cd10431cc --- /dev/null +++ b/src/tests/kits/net/sock/writen.c @@ -0,0 +1,28 @@ +/* -*- c-basic-offset: 8; -*- */ +/* include writen */ +#include "sock.h" + +ssize_t /* Write "n" bytes to a descriptor. */ +writen(int fd, const void *vptr, size_t n) +{ + size_t nleft; + ssize_t nwritten; + const char *ptr; + + ptr = vptr; + nleft = n; + while (nleft > 0) { + if ( (nwritten = write(fd, ptr, nleft)) <= 0) { + if (errno == EINTR) + nwritten = 0; /* and call write() again */ + else + return(-1); /* error */ + } + + nleft -= nwritten; + ptr += nwritten; + } + return(n); +} +/* end writen */ +