1/*
2 * Copyright 2009, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6//!	A test app trying to reproduce bug #2197.
7
8#include <errno.h>
9#include <fcntl.h>
10#include <netinet/in.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <sys/select.h>
15#include <sys/socket.h>
16
17
18static int
19open_tcp_socket()
20{
21	int fd = socket(AF_INET, SOCK_STREAM, 0);
22	if (fd < 0)
23		return -1;
24
25	// make it non-blocking
26	fcntl(fd, F_SETFL, O_NONBLOCK);
27
28	return fd;
29}
30
31
32static void
33init_sockaddr(struct sockaddr_in& address, unsigned ipAddress, unsigned port)
34{
35	memset(&address, 0, sizeof(sockaddr_in));
36	address.sin_family = AF_INET;
37	address.sin_port = htons(port);
38	address.sin_addr.s_addr = htonl(ipAddress);
39}
40
41
42static int
43tcp_pair(int pair[])
44{
45	pair[0] = pair[1] = -1;
46
47	int listenSocket = open_tcp_socket();
48	if (listenSocket < 0)
49		return -1;
50
51	sockaddr_in listenAddress;
52	sockaddr_in peerAddress;
53	sockaddr_in address;
54	socklen_t length;
55	init_sockaddr(listenAddress, INADDR_LOOPBACK, 0);
56
57	if (bind(listenSocket, (sockaddr*)&listenAddress, sizeof(sockaddr_in)) != 0)
58		goto error;
59
60	length = sizeof(sockaddr_in);
61	if (getsockname(listenSocket, (sockaddr*)&listenAddress, &length) != 0)
62		goto error;
63
64	if (listen(listenSocket, 5) != 0)
65		goto error;
66
67	pair[0] = open_tcp_socket();
68	if (pair[0] < 0)
69		goto error;
70
71	init_sockaddr(address, INADDR_LOOPBACK, ntohs(listenAddress.sin_port));
72
73	if (connect(pair[0], (sockaddr*)&address, sizeof(sockaddr_in)) != 0
74		&& errno != EINPROGRESS)
75		goto error;
76
77	if (errno == EINPROGRESS) {
78		struct timeval tv;
79		tv.tv_sec = 100000;
80		tv.tv_usec = 0;
81		fd_set set;
82		FD_ZERO(&set);
83		FD_SET(pair[0], &set);
84		if (select(pair[0] + 1, NULL, &set, NULL, &tv) < 0)
85			fprintf(stderr, "write select() failed: %s\n", strerror(errno));
86	}
87
88	length = sizeof(sockaddr_in);
89	if (getsockname(pair[0], (sockaddr*)&address, &length) != 0)
90		goto error;
91
92	while (true) {
93		pair[1] = accept(listenSocket, (sockaddr*)&peerAddress, &length);
94		if (pair[1] >= 0)
95			break;
96
97		if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ETIMEDOUT)
98			goto error;
99
100		struct timeval tv;
101		tv.tv_sec = 100000;
102		tv.tv_usec = 0;
103		fd_set set;
104		FD_ZERO(&set);
105		FD_SET(listenSocket, &set);
106		if (select(listenSocket + 1, &set, NULL, NULL, &tv) < 0)
107			fprintf(stderr, "read select() failed: %s\n", strerror(errno));
108	}
109
110	if (peerAddress.sin_port != address.sin_port)
111		goto error;
112
113	close(listenSocket);
114    return 0;
115
116error:
117	close(listenSocket);
118	for (int i = 0; i < 2; i++) {
119		if (pair[i] >= 0)
120			close(pair[i]);
121	}
122
123    return -1;
124}
125
126
127int
128main(int argc, char** argv)
129{
130	int pair[2];
131	if (tcp_pair(pair) == 0) {
132		close(pair[0]);
133		close(pair[1]);
134	} else
135		fprintf(stderr, "pair failed: %s\n", strerror(errno));
136	return 0;
137}
138
139