Ticket #9734: socket_test_send_double_recv_1_socket.c

File socket_test_send_double_recv_1_socket.c, 5.6 KB (added by markh, 6 years ago)
Line 
1#include <stdlib.h>
2#include <netdb.h>
3#include <sys/select.h>
4#include <stdio.h>
5#include <errno.h>
6#include <fcntl.h>
7
8static struct sockaddr_storage pgStatAddr;
9
10typedef int bool;
11
12bool
13set_noblock(int sock)
14{
15 return (fcntl(sock, F_SETFL, O_NONBLOCK) != -1);
16}
17
18void
19socket_test_send_recv(void)
20{
21
22 int pgStatSock = -1;
23 socklen_t alen;
24 struct addrinfo *addrs = NULL,
25 *addr,
26 hints;
27 int ret;
28 fd_set rset;
29 struct timeval tv;
30 char test_byte;
31 int sel_res;
32 bool retry = 1;
33 int len;
34
35#define TESTBYTEVAL ((char) 199)
36
37 /*
38 * Create the UDP socket for sending and receiving statistic messages
39 */
40 hints.ai_flags = AI_PASSIVE;
41 hints.ai_family = PF_UNSPEC;
42 hints.ai_socktype = SOCK_DGRAM;
43 hints.ai_protocol = 0;
44 hints.ai_addrlen = 0;
45 hints.ai_addr = NULL;
46 hints.ai_canonname = NULL;
47 hints.ai_next = NULL;
48 ret = getaddrinfo("localhost", NULL, &hints, &addrs);
49 if (ret || !addrs) {
50 printf("could not resolve \"localhost\": %s\n", gai_strerror(ret));
51 }
52 else {
53
54 /*
55 * Loop through the results to try and find a working address.
56 */
57 for (addr = addrs; addr; addr = addr->ai_next)
58 {
59 /*
60 * Create the socket.
61 */
62 if ((pgStatSock = socket(addr->ai_family, SOCK_DGRAM, 0)) == -1)
63 {
64 printf("could not create socket for statistics collector\n");
65 continue;
66 }
67
68 /*
69 * Bind it to a kernel assigned port on localhost and get the assigned
70 * port via getsockname().
71 */
72 if (bind(pgStatSock, addr->ai_addr, addr->ai_addrlen) < 0)
73 {
74 printf("could not bind socket for statistics collector\n");
75 closesocket(pgStatSock);
76 pgStatSock = -1;
77 continue;
78 }
79
80 alen = sizeof(pgStatAddr);
81 if (getsockname(pgStatSock, (struct sockaddr *) & pgStatAddr, &alen) < 0)
82 {
83 printf("could not get address of socket for statistics collector\n");
84 closesocket(pgStatSock);
85 pgStatSock = -1;
86 continue;
87 }
88
89 /*
90 * Connect the socket to its own address. This saves a few cycles by
91 * not having to respecify the target address on every send. This also
92 * provides a kernel-level check that only packets from this same
93 * address will be received.
94 */
95 if (connect(pgStatSock, (struct sockaddr *) & pgStatAddr, alen) < 0)
96 {
97 printf("could not connect socket for statistics collector\n");
98 closesocket(pgStatSock);
99 pgStatSock = -1;
100 continue;
101 }
102
103 /*
104 * Try to send and receive a one-byte test message on the socket. This
105 * is to catch situations where the socket can be created but will not
106 * actually pass data (for instance, because kernel packet filtering
107 * rules prevent it).
108 */
109 test_byte = TESTBYTEVAL;
110
111 while (retry) {
112 if (send(pgStatSock, &test_byte, 1, 0) != 1) {
113 /* if not interrupted, bail out */
114 if (errno != EINTR) {
115 retry = 0;
116 printf("could not send test message on socket for statistics collector\n");
117 closesocket(pgStatSock);
118 pgStatSock = -1;
119 continue;
120 }
121 }
122 else {
123 retry = 0;
124 }
125 }
126
127 /*
128 * There could possibly be a little delay before the message can be
129 * received. We arbitrarily allow up to half a second before deciding
130 * it's broken.
131 */
132 for (;;) /* need a loop to handle EINTR */
133 {
134 FD_ZERO(&rset);
135 FD_SET(pgStatSock, &rset);
136
137 tv.tv_sec = 0;
138 tv.tv_usec = 500000;
139 sel_res = select(pgStatSock + 1, &rset, NULL, NULL, &tv);
140 if (sel_res >= 0 || errno != EINTR)
141 break;
142 }
143 if (sel_res < 0)
144 {
145 printf("select() failed in statistics collector\n");
146 closesocket(pgStatSock);
147 pgStatSock = -1;
148 continue;
149 }
150 if (sel_res == 0 || !FD_ISSET(pgStatSock, &rset))
151 {
152 /*
153 * This is the case we actually think is likely, so take pains to
154 * give a specific message for it.
155 *
156 * errno will not be set meaningfully here, so don't use it.
157 */
158 printf("test message did not get through on socket for statistics collector\n");
159 closesocket(pgStatSock);
160 pgStatSock = -1;
161 continue;
162 }
163
164 test_byte++; /* just make sure variable is changed */
165
166 retry = 1;
167 while (retry) {
168 if (recv(pgStatSock, &test_byte, 1, 0) != 1) {
169 /* if not interrupted, bail out */
170 if (errno != EINTR) {
171 retry = 0;
172 printf("Errno: %i\n", errno);
173 printf("String error: %s\n", strerror(errno));
174 printf("could not receive test message on socket for statistics collector\n");
175 closesocket(pgStatSock);
176 pgStatSock = -1;
177 continue;
178 }
179 }
180 else {
181 retry = 0;
182 }
183 }
184
185 if (test_byte != TESTBYTEVAL) /* strictly paranoia ... */
186 {
187 printf("incorrect test message transmission on socket for statistics collector\n");
188 closesocket(pgStatSock);
189 pgStatSock = -1;
190 continue;
191 }
192
193 /* If we get here, we have a working socket */
194 printf("We have a working socket!\n");
195
196 /*
197 * Set the socket to non-blocking IO.
198 */
199 if (!set_noblock(pgStatSock))
200 {
201 printf("could not set statistics collector socket to nonblocking mode\n");
202 }
203 else
204 {
205
206 /* Try to read from it again without any data waiting... */
207 len = recv(pgStatSock, &test_byte, 1, 0);
208
209 if (len < 0)
210 {
211 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
212 printf("Breaking out of loop. No data as expected\n");
213 break; /* out of inner loop */
214 }
215 printf("Unexpected error: %s\n", strerror(errno));
216 break;
217 }
218 else {
219 printf("Received data! Should not be possible\n");
220 }
221 }
222
223 if (pgStatSock > -1) {
224 closesocket(pgStatSock);
225 }
226
227 break;
228 }
229 }
230
231 if (addrs != NULL)
232 freeaddrinfo(addrs);
233
234}
235
236int main(int argc, char **argv) {
237
238 socket_test_send_recv();
239
240 return 0;
241
242}