1/*-
2 * Copyright (c) 2004 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29/*
30 * tcpstream sets up a simple TCP client and server, and then streams a
31 * predictable pseudo-random byte sequence through it using variable block
32 * sizes.  The intent is to to detect corruption of data in the TCP stream.
33 */
34
35#include <sys/types.h>
36#include <sys/errno.h>
37#include <sys/socket.h>
38
39#include <netinet/in.h>
40
41#include <arpa/inet.h>
42
43#include <err.h>
44#include <errno.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <unistd.h>
49
50#define	MAX_LOOPS	10240
51#define	MAX_LONGS	1024
52
53static void
54usage(void)
55{
56
57	fprintf(stderr, "tcpstream client [ip] [port] [seed]\n");
58	fprintf(stderr, "tcpstream server [port] [seed]\n");
59	exit(-1);
60}
61
62static void
63fill_buffer(long *buffer, int len)
64{
65	int i;
66
67	for (i = 0; i < len; i++)
68		buffer[i] = htonl(random());
69}
70
71static int
72check_buffer(long *buffer, int len)
73{
74	int i;
75
76	for (i = 0; i < len; i++) {
77		if (buffer[i] != htonl(random()))
78			return (0);
79	}
80	return (1);
81}
82
83static void
84tcpstream_client(struct sockaddr_in sin, long seed)
85{
86	long buffer[MAX_LONGS];
87	ssize_t len;
88	int i, j, sock;
89
90	srandom(seed);
91
92	sock = socket(PF_INET, SOCK_STREAM, 0);
93	if (sock == -1)
94		errx(-1, "socket: %s", strerror(errno));
95
96	if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) == -1)
97		errx(-1, "connect: %s", strerror(errno));
98
99	for (j = 0; j < MAX_LOOPS; j++) {
100		for (i = 0; i < MAX_LONGS; i++) {
101			fill_buffer(buffer, i);
102			len = send(sock, buffer, i * sizeof(long), 0);
103			if (len == -1) {
104				printf("%d bytes written of %d expected\n",
105				    len, i * sizeof(long));
106				fflush(stdout);
107				perror("send");
108				goto done;
109			}
110		}
111	}
112
113done:
114	close(sock);
115}
116
117static void
118tcpstream_server(struct sockaddr_in sin, long seed)
119{
120	int i, j, listen_sock, accept_sock;
121	struct sockaddr_in other_sin;
122	long buffer[MAX_LONGS];
123	socklen_t addrlen;
124	ssize_t len;
125
126	int input_byte_counter;
127
128	listen_sock = socket(PF_INET, SOCK_STREAM, 0);
129	if (listen_sock == -1)
130		errx(-1, "socket: %s", strerror(errno));
131
132	if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) == -1)
133		errx(-1, "bind: %s", strerror(errno));
134
135	if (listen(listen_sock, -1) == -1)
136		errx(-1, "listen: %s", strerror(errno));
137
138	while (1) {
139		bzero(&other_sin, sizeof(other_sin));
140		addrlen = sizeof(other_sin);
141
142		accept_sock = accept(listen_sock, (struct sockaddr *)
143		    &other_sin, &addrlen);
144		if (accept_sock == -1) {
145			perror("accept");
146			continue;
147		}
148		printf("connection opened from %s:%d\n",
149		    inet_ntoa(other_sin.sin_addr), ntohs(other_sin.sin_port));
150		input_byte_counter = 0;
151
152		srandom(seed);
153
154		for (j = 0; j < MAX_LOOPS; j++) {
155			for (i = 0; i < MAX_LONGS; i++) {
156				len = recv(accept_sock, buffer,
157				    i * sizeof(long), MSG_WAITALL);
158				if (len != i * sizeof(long)) {
159					perror("recv");
160					goto done;
161				}
162				if (check_buffer(buffer, i) == 0) {
163					fprintf(stderr,
164    "Corruption in block beginning %d and ending %d\n", input_byte_counter,
165    input_byte_counter + len);
166					fprintf(stderr,
167    "Block size %d\n", i * sizeof(long));
168					goto done;
169				}
170				input_byte_counter += len;
171			}
172		}
173done:
174		printf("connection closed\n");
175		close(accept_sock);
176	}
177}
178
179int
180main(int argc, char *argv[])
181{
182	struct sockaddr_in sin;
183	long port, seed;
184	char *dummy;
185
186	if (argc < 2)
187		usage();
188	if (strcmp(argv[1], "client") == 0) {
189		if (argc != 5)
190			usage();
191
192		bzero(&sin, sizeof(sin));
193		sin.sin_len = sizeof(sin);
194		sin.sin_family = AF_INET;
195
196		if (inet_aton(argv[2], &sin.sin_addr) != 1)
197			errx(-1, "%s: %s", argv[2], strerror(EINVAL));
198
199		port = strtoul(argv[3], &dummy, 10);
200		if (port < 1 || port > 65535 || *dummy != '\0')
201			usage();
202		sin.sin_port = htons(port);
203
204		seed = strtoul(argv[4], &dummy, 10);
205		if (*dummy != '\0')
206			usage();
207
208		tcpstream_client(sin, seed);
209
210	} else if (strcmp(argv[1], "server") == 0) {
211		if (argc != 4)
212			usage();
213
214		bzero(&sin, sizeof(sin));
215		sin.sin_len = sizeof(sin);
216		sin.sin_family = AF_INET;
217		sin.sin_addr.s_addr = INADDR_ANY;
218
219		port = strtoul(argv[2], &dummy, 10);
220		if (port < 1 || port > 65535 || *dummy != '\0')
221			usage();
222		sin.sin_port = htons(port);
223
224		seed = strtoul(argv[3], &dummy, 10);
225		if (*dummy != '\0')
226			usage();
227
228		tcpstream_server(sin, seed);
229	} else
230		usage();
231
232	return (0);
233}
234