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}