xref: /minix3/minix/tests/t40e.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1 /* t40e.c
2  *
3  * Test sockets
4  *
5  * Select works on regular files, (pseudo) terminal devices, streams-based
6  * files, FIFOs, pipes, and sockets. This test verifies selecting for sockets.
7  *
8  * This test is part of a bigger select test. It expects as argument which sub-
9  * test it is.
10  *
11  * Specific rules for sockets:
12  * If a socket has a pending error, it shall be considered to have an
13  * exceptional condition pending. Otherwise, what constitutes an exceptional
14  * condition is file type-specific. For a file descriptor for use with a
15  * socket, it is protocol-specific except as noted below. For other file types
16  * it is implementation-defined. If the operation is meaningless for a
17  * particular file type, pselect() or select() shall indicate that the
18  * descriptor is ready for read or write operations, and shall indicate that
19  * the descriptor has no exceptional condition pending.
20  *
21  * [1] If a descriptor refers to a socket, the implied input function is the
22  * recvmsg()function with parameters requesting normal and ancillary data, such
23  * that the presence of either type shall cause the socket to be marked as
24  * readable. The presence of out-of-band data shall be checked if the socket
25  * option SO_OOBINLINE has been enabled, as out-of-band data is enqueued with
26  * normal data. If the socket is currently listening, then it shall be marked
27  * as readable if an incoming connection request has been received, and a call
28  * to the accept() function shall complete without blocking.
29  *
30  * [2] If a descriptor refers to a socket, the implied output function is the
31  * sendmsg() function supplying an amount of normal data equal to the current
32  * value of the SO_SNDLOWAT option for the socket. If a non-blocking call to
33  * the connect() function has been made for a socket, and the connection
34  * attempt has either succeeded or failed leaving a pending error, the socket
35  * shall be marked as writable.
36  *
37  * [3] A socket shall be considered to have an exceptional condition pending if
38  * a receive operation with O_NONBLOCK clear for the open file description and
39  * with the MSG_OOB flag set would return out-of-band data without blocking.
40  * (It is protocol-specific whether the MSG_OOB flag would be used to read
41  * out-of-band data.) A socket shall also be considered to have an exceptional
42  * condition pending if an out-of-band data mark is present in the receive
43  * queue. Other circumstances under which a socket may be considered to have an
44  * exceptional condition pending are protocol-specific and
45  * implementation-defined.
46  */
47 
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <sys/wait.h>
54 #include <sys/select.h>
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
58 #include <fcntl.h>
59 #include <errno.h>
60 #include <string.h>
61 #include <time.h>
62 #include <assert.h>
63 #include <netdb.h>
64 
65 #include "common.h"
66 
67 #define DO_HANDLEDATA 1
68 #define DO_PAUSE 3
69 #define DO_TIMEOUT 7
70 #define MYPORT 3490
71 #define NUMCHILDREN 5
72 #define MAX_ERROR 10
73 
74 char errbuf[1000];
75 
76 /* All *_fds routines are helping routines. They intentionally use FD_* macros
77    in order to prevent making assumptions on how the macros are implemented.*/
78 
79 #if 0
80 static int count_fds(int nfds, fd_set *fds) {
81   /* Return number of bits set in fds */
82   int i, result = 0;
83   assert(fds != NULL && nfds > 0);
84   for(i = 0; i < nfds; i++) {
85     if(FD_ISSET(i, fds)) result++;
86   }
87   return result;
88 }
89 #endif
90 
empty_fds(int nfds,fd_set * fds)91 static int empty_fds(int nfds, fd_set *fds) {
92   /* Returns nonzero if the first bits up to nfds in fds are not set */
93   int i;
94   assert(fds != NULL && nfds > 0);
95   for(i = 0; i < nfds; i++) if(FD_ISSET(i, fds)) return 0;
96   return 1;
97 }
98 
compare_fds(int nfds,fd_set * lh,fd_set * rh)99 static int compare_fds(int nfds, fd_set *lh, fd_set *rh) {
100   /* Returns nonzero if lh equals rh up to nfds bits */
101   int i;
102   assert(lh != NULL && rh != NULL && nfds > 0);
103   for(i = 0; i < nfds; i++) {
104     if((FD_ISSET(i, lh) && !FD_ISSET(i, rh)) ||
105        (!FD_ISSET(i, lh) && FD_ISSET(i, rh))) {
106       return 0;
107     }
108   }
109   return 1;
110 }
111 
112 #if 0
113 static void dump_fds(int nfds, fd_set *fds) {
114   /* Print a graphical representation of bits in fds */
115   int i;
116   if(fds != NULL && nfds > 0) {
117     for(i = 0; i < nfds; i++) printf("%d ", (FD_ISSET(i, fds) ? 1 : 0));
118     printf("\n");
119   }
120 }
121 #endif
122 
do_child(int childno)123 static void do_child(int childno) {
124   int fd_sock, port;
125   int retval;
126 
127   fd_set fds_read, fds_write, fds_error;
128   fd_set fds_compare_write;
129 
130   struct hostent *he;
131   struct sockaddr_in server;
132   struct timeval tv;
133 
134   if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
135     perror("Error getting socket\n");
136     exit(-1);
137   }
138 
139   if((he = gethostbyname("127.0.0.1")) == NULL){/*"localhost" might be unknown*/
140     perror("Error resolving");
141     exit(-1);
142   }
143 
144   /* Child 4 connects to the wrong port. See Actual testing description below.*/
145   port = (childno == 3 ? MYPORT + 1 : MYPORT);
146 
147   memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
148   server.sin_family = AF_INET;
149   server.sin_port = htons(port);
150 
151 #if 0
152   printf("Going to connect to: %s:%d\n", inet_ntoa(server.sin_addr),
153 	 ntohs(server.sin_port));
154 #endif
155 
156   /* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
157 #if !defined(__minix)
158   memset(server.sin_zero, '\0', sizeof server.sin_zero);
159 #endif
160 
161   /* Wait for parent to set up connection */
162   tv.tv_sec = (childno <= 1 ? DO_PAUSE : DO_TIMEOUT);
163   tv.tv_usec = 0;
164   retval = select(0, NULL, NULL, NULL, &tv);
165 
166   /* All set, let's do some testing */
167   /* Children 3 and 4 do a non-blocking connect */
168   if(childno == 2 || childno == 3)
169     fcntl(fd_sock, F_SETFL, fcntl(fd_sock, F_GETFL, 0) | O_NONBLOCK);
170 
171   if(connect(fd_sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
172     /* Well, we don't actually care. The connect is non-blocking and is
173        supposed to "in progress" at this point. */
174   }
175 
176   if(childno == 2 || childno == 3) { /* Children 3 and 4 */
177     /* Open Group: "If a non-blocking call to the connect() function has been
178        made for a socket, and the connection attempt has either succeeded or
179        failed leaving a pending error, the socket shall be marked as writable.
180        ...
181        A socket shall be considered to have an exceptional condition pending if
182        a receive operation with O_NONBLOCK clear for the open file description
183        and with the MSG_OOB flag set would return out-of-band data without
184        blocking. (It is protocol-specific whether the MSG_OOB flag would be used
185        to read out-of-band data.) A socket shall also be considered to have an
186        exceptional condition pending if an out-of-band data mark is present in
187        the receive queue. Other circumstances under which a socket may be
188        considered to have an exceptional condition pending are protocol-specific
189        and implementation-defined."
190 
191        In other words, it only makes sense for us to check the write set as the
192        read set is not expected to be set, but is allowed to be set (i.e.,
193        unspecified) and whether the error set is set is implementation-defined.
194     */
195     FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
196     FD_SET(fd_sock, &fds_write);
197     tv.tv_sec = DO_TIMEOUT;
198     tv.tv_usec = 0;
199     retval = select(fd_sock+1, NULL, &fds_write, NULL, &tv);
200 
201 
202     if(retval <= 0) em(6, "expected one fd to be ready");
203 
204     FD_ZERO(&fds_compare_write); FD_SET(fd_sock, &fds_compare_write);
205     if(!compare_fds(fd_sock+1, &fds_compare_write, &fds_compare_write))
206       em(7, "write should be set");
207   }
208 
209   if(close(fd_sock) < 0) {
210     perror("Error disconnecting");
211     exit(-1);
212   }
213 
214   exit(errct);
215 }
216 
do_parent(void)217 static void do_parent(void) {
218 #if !defined(__minix)
219   int yes = 1;
220 #endif
221   int fd_sock, fd_new, exitstatus;
222   int sockets[NUMCHILDREN], i;
223   fd_set fds_read, fds_write, fds_error;
224   fd_set fds_compare_read, fds_compare_write;
225   struct timeval tv;
226   int retval, childresults = 0;
227 
228   struct sockaddr_in my_addr;
229   struct sockaddr_in other_addr;
230   socklen_t other_size;
231 
232   if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
233     perror("Error getting socket\n");
234     exit(-1);
235   }
236 
237   my_addr.sin_family = AF_INET;
238   my_addr.sin_port = htons(MYPORT); /* Short, network byte order */
239   my_addr.sin_addr.s_addr = INADDR_ANY;
240   /* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
241 #if !defined(__minix)
242   memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
243 #endif
244 
245   /* Reuse port number. Not implemented in Minix. */
246 #if !defined(__minix)
247   if(setsockopt(fd_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
248     perror("Error setting port reuse option");
249     exit(-1);
250   }
251 #endif
252 
253   /* Bind to port */
254   if(bind(fd_sock, (struct sockaddr *) &my_addr, sizeof my_addr) < 0) {
255     perror("Error binding to port");
256     exit(-1);
257   }
258 
259   /* Mark socket to be used for incoming connections */
260   if(listen(fd_sock, 20) < 0) {
261     perror("Listen");
262     exit(-1);
263   }
264 
265   /*                              Actual testing                              */
266   /* While sockets resemble file descriptors, they are not the same at all.
267      We can read/write from/to and close file descriptors, but we cannot open
268      them O_RDONLY or O_WRONLY; they are always O_RDWR (other flags do not make
269      sense regarding sockets). As such, we cannot provide wrong file descriptors
270      to select, except for descriptors that are not in use.
271      We will test standard behavior and what is described in [2]. [1] and [3]
272      are not possible to test on Minix, as Minix does not support OOB data. That
273      is, the TCP layer can handle it, but there is no socket interface for it.
274      Our test consists of waiting for input from the first two children and
275      waiting to write output [standard usage]. Then the first child closes its
276      connection we select for reading. This should fail with error set. Then we
277      close child number two on our side and select for reading. This should fail
278      with EBADF. Child number three shall then do a non-blocking connect (after
279      waiting for DO_PAUSE seconds) and do a select, resulting in being marked
280      ready for writing. Subsequently child number four also does a non-blocking
281      connect to loclhost on MYPORT+1 (causing the connect to fail) and then does
282      a select. This should result in write and error being set (error because of
283      pending error).
284   */
285 
286   /* Accept and store connections from the first two children */
287   other_size = sizeof(other_addr);
288   for(i = 0; i < 2; i++) {
289     fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
290     if(fd_new < 0) break;
291     sockets[i] = fd_new;
292   }
293 
294   /* If we break out of the for loop, we ran across an error and want to exit.
295      Check whether we broke out. */
296   if(fd_new < 0) {
297     perror("Error accepting connection");
298     exit(-1);
299   }
300 
301   /* Select error condition checking */
302   for(childresults = 0; childresults < 2; childresults++) {
303     FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
304     FD_SET(sockets[childresults], &fds_read);
305     FD_SET(sockets[childresults], &fds_write);
306     FD_SET(sockets[childresults], &fds_error);
307     tv.tv_sec = DO_TIMEOUT;
308     tv.tv_usec = 0;
309 
310     retval = select(sockets[childresults]+1, &fds_read, &fds_write, &fds_error,
311 		    &tv);
312 
313     if(retval <= 0) {
314       snprintf(errbuf, sizeof(errbuf),
315 	       "two fds should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
316       em(1, errbuf);
317     }
318 
319     FD_ZERO(&fds_compare_read); FD_ZERO(&fds_compare_write);
320     FD_SET(sockets[childresults], &fds_compare_write);
321 
322     /* We can't say much about being ready for reading at this point or not. It
323        is not specified and the other side might have data ready for us to read
324     */
325     if(!compare_fds(sockets[childresults]+1, &fds_compare_write, &fds_write))
326       em(2, "write should be set");
327 
328     if(!empty_fds(sockets[childresults]+1, &fds_error))
329       em(3, "no error should be set");
330   }
331 
332 
333   /* We continue by accepting a connection of child 3 */
334   fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
335   if(fd_new < 0) {
336     perror("Error accepting connection\n");
337     exit(-1);
338   }
339   sockets[2] = fd_new;
340 
341   /* Child 4 will never connect */
342 
343   /* Child 5 is still pending to be accepted. Open Group: "If the socket is
344      currently listening, then it shall be marked as readable if an incoming
345      connection request has been received, and a call to the accept() function
346      shall complete without blocking."*/
347   FD_ZERO(&fds_read);
348   FD_SET(fd_sock, &fds_read);
349   tv.tv_sec = DO_TIMEOUT;
350   tv.tv_usec = 0;
351   retval = select(fd_sock+1, &fds_read, NULL, NULL, &tv);
352   if(retval <= 0) {
353     snprintf(errbuf, sizeof(errbuf),
354 	     "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
355     em(4, errbuf);
356   }
357 
358   /* Check read bit is set */
359   FD_ZERO(&fds_compare_read); FD_SET(fd_sock, &fds_compare_read);
360   if(!compare_fds(fd_sock+1, &fds_compare_read, &fds_read))
361     em(5, "read should be set");
362 
363 
364   /* Accept incoming connection to unblock child 5 */
365   fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
366   if(fd_new < 0) {
367     perror("Error accepting connection\n");
368     exit(-1);
369   }
370   sockets[4] = fd_new;
371 
372 
373   /* We're done, let's wait a second to synchronize children and parent. */
374   tv.tv_sec = DO_HANDLEDATA;
375   tv.tv_usec = 0;
376   select(0, NULL, NULL, NULL, &tv);
377 
378   /* Close connection with children. */
379   for(i = 0; i < NUMCHILDREN; i++) {
380     if(i == 3) /* No need to disconnect child 4 that failed to connect. */
381       continue;
382 
383     if(close(sockets[i]) < 0) {
384       perror(NULL);
385     }
386   }
387 
388   /* Close listening socket */
389   if(close(fd_sock) < 0) {
390     perror("Closing listening socket");
391     errct++;
392   }
393 
394   for(i = 0; i < NUMCHILDREN; i++) {
395     wait(&exitstatus); /* Wait for children */
396     if(exitstatus > 0)
397       errct += WEXITSTATUS(exitstatus); /* and count their errors, too. */
398   }
399 
400   exit(errct);
401 }
402 
main(int argc,char ** argv)403 int main(int argc, char **argv) {
404   int forkres, i;
405 
406   /* Get subtest number */
407   if(argc != 2) {
408     printf("Usage: %s subtest_no\n", argv[0]);
409     exit(-2);
410   } else if(sscanf(argv[1], "%d", &subtest) != 1) {
411     printf("Usage: %s subtest_no\n", argv[0]);
412     exit(-2);
413   }
414 
415   /* Fork off a bunch of children */
416   for(i = 0; i < NUMCHILDREN; i++) {
417       forkres = fork();
418       if(forkres == 0) do_child(i);
419       else if(forkres < 0) {
420 	perror("Unable to fork");
421 	exit(-1);
422       }
423   }
424   /* do_child always calls exit(), so when we end up here, we're the parent. */
425   do_parent();
426 
427   exit(-2); /* We're not supposed to get here. Both do_* routines should exit.*/
428 }
429