fa5dcf50394c3007a4dedcd57b3e135799d04121
[oweals/gnunet.git] / src / util / network.c
1 /*\r
2      This file is part of GNUnet.\r
3      (C) 2009 Christian Grothoff (and other contributing authors)\r
4 \r
5      GNUnet is free software; you can redistribute it and/or modify\r
6      it under the terms of the GNU General Public License as published\r
7      by the Free Software Foundation; either version 2, or (at your\r
8      option) any later version.\r
9 \r
10      GNUnet is distributed in the hope that it will be useful, but\r
11      WITHOUT ANY WARRANTY; without even the implied warranty of\r
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
13      General Public License for more details.\r
14 \r
15      You should have received a copy of the GNU General Public License\r
16      along with GNUnet; see the file COPYING.  If not, write to the\r
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,\r
18      Boston, MA 02111-1307, USA.\r
19 */\r
20 \r
21 /**\r
22  * @file util/network.c\r
23  * @brief basic, low-level networking interface\r
24  * @author Nils Durner\r
25  */\r
26 \r
27 #include "platform.h"\r
28 #include "gnunet_disk_lib.h"\r
29 #include "disk.h"\r
30 #include "gnunet_container_lib.h"\r
31 \r
32 #define DEBUG_SOCK GNUNET_NO\r
33 \r
34 struct GNUNET_NETWORK_Handle\r
35 {\r
36   int fd;\r
37 };\r
38 \r
39 struct GNUNET_NETWORK_FDSet\r
40 {\r
41   /* socket descriptors */\r
42   int nsds;\r
43   fd_set sds;\r
44 #ifdef WINDOWS\r
45   /* handles */\r
46   struct GNUNET_CONTAINER_SList *handles;\r
47 #endif\r
48 };\r
49 \r
50 #ifndef FD_COPY\r
51 #define FD_COPY(s, d) (memcpy ((d), (s), sizeof (fd_set)))\r
52 #endif\r
53 \r
54 /**\r
55  * accept a new connection on a socket\r
56  *\r
57  * @param desc bound socket\r
58  * @param address address of the connecting peer, may be NULL\r
59  * @param address_len length of address\r
60  * @return client socket\r
61  */\r
62 struct GNUNET_NETWORK_Handle *\r
63 GNUNET_NETWORK_socket_accept (const struct GNUNET_NETWORK_Handle *desc,\r
64                               struct sockaddr *address,\r
65                               socklen_t * address_len)\r
66 {\r
67   struct GNUNET_NETWORK_Handle *ret;\r
68 \r
69   ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle));\r
70   ret->fd = accept (desc->fd, address, address_len);\r
71 #ifdef MINGW\r
72   if (INVALID_SOCKET == ret->fd)\r
73     SetErrnoFromWinsockError (WSAGetLastError ());\r
74 #endif\r
75   return ret;\r
76 }\r
77 \r
78 /**\r
79  * Bind to a connected socket\r
80  * @param desc socket\r
81  * @param address address to be bound\r
82  * @param address_len length of address\r
83  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
84  */\r
85 int\r
86 GNUNET_NETWORK_socket_bind (struct GNUNET_NETWORK_Handle *desc,\r
87                             const struct sockaddr *address,\r
88                             socklen_t address_len)\r
89 {\r
90   int ret;\r
91 \r
92   ret = bind (desc->fd, address, address_len);\r
93 #ifdef MINGW\r
94   if (SOCKET_ERROR == ret)\r
95     SetErrnoFromWinsockError (WSAGetLastError ());\r
96 #endif\r
97   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
98 }\r
99 \r
100 /**\r
101  * Set if a socket should use blocking or non-blocking IO.\r
102  * @param fd socket\r
103  * @param doBlock blocking mode\r
104  * @return GNUNET_OK on success, GNUNET_SYSERR on error\r
105  */\r
106 int\r
107 GNUNET_NETWORK_socket_set_blocking (struct GNUNET_NETWORK_Handle *fd,\r
108                                     int doBlock)\r
109 {\r
110 #if MINGW\r
111   u_long mode;\r
112   mode = !doBlock;\r
113   if (ioctlsocket (fd->fd, FIONBIO, &mode) == SOCKET_ERROR)\r
114     {\r
115       SetErrnoFromWinsockError (WSAGetLastError ());\r
116       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket");\r
117       return GNUNET_SYSERR;\r
118     }\r
119   return GNUNET_OK;\r
120 \r
121 #else\r
122   /* not MINGW */\r
123   int flags = fcntl (fd->fd, F_GETFL);\r
124   if (flags == -1)\r
125     {\r
126       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl");\r
127       return GNUNET_SYSERR;\r
128     }\r
129   if (doBlock)\r
130     flags &= ~O_NONBLOCK;\r
131   else\r
132     flags |= O_NONBLOCK;\r
133   if (0 != fcntl (fd->fd, F_SETFL, flags))\r
134     {\r
135       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl");\r
136       return GNUNET_SYSERR;\r
137     }\r
138   return GNUNET_OK;\r
139 #endif\r
140 }\r
141 \r
142 /**\r
143  * Close a socket\r
144  * @param desc socket\r
145  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
146  */\r
147 int\r
148 GNUNET_NETWORK_socket_close (struct GNUNET_NETWORK_Handle *desc)\r
149 {\r
150   int ret;\r
151 #ifdef MINGW\r
152   ret = closesocket (desc->fd);\r
153   if (SOCKET_ERROR != ret)\r
154     GNUNET_free (desc);\r
155   else\r
156     SetErrnoFromWinsockError (WSAGetLastError ());\r
157 #else\r
158   /* FIXME: Nils, this is very strange code here... */\r
159   ret = close (desc->fd);\r
160   if (0 == ret)\r
161     GNUNET_free (desc);\r
162 #endif\r
163 \r
164   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
165 }\r
166 \r
167 /**\r
168  * Connect a socket\r
169  * @param desc socket\r
170  * @param address peer address\r
171  * @param address_len length of address\r
172  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
173  */\r
174 int\r
175 GNUNET_NETWORK_socket_connect (const struct GNUNET_NETWORK_Handle *desc,\r
176                                const struct sockaddr *address,\r
177                                socklen_t address_len)\r
178 {\r
179   int ret;\r
180 \r
181   ret = connect (desc->fd, address, address_len);\r
182 #ifdef MINGW\r
183   if (SOCKET_ERROR == ret)\r
184     {\r
185       SetErrnoFromWinsockError (WSAGetLastError ());\r
186       if (errno == EWOULDBLOCK)\r
187         errno = EINPROGRESS;\r
188     }\r
189 #endif\r
190   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
191 }\r
192 \r
193 /**\r
194  * Get socket options\r
195  * @param desc socket\r
196  * @param level protocol level of the option\r
197  * @param optname identifier of the option\r
198  * @param optval options\r
199  * @param optlen length of optval\r
200  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
201  */\r
202 int\r
203 GNUNET_NETWORK_socket_getsockopt (const struct GNUNET_NETWORK_Handle *desc,\r
204                                   int level, int optname, void *optval,\r
205                                   socklen_t * optlen)\r
206 {\r
207   int ret;\r
208 \r
209   ret = getsockopt (desc->fd, level, optname, optval, optlen);\r
210 #ifdef MINGW\r
211   if (ret == 0 && level == SOL_SOCKET && optname == SO_ERROR)\r
212     *((int *) optval) = GetErrnoFromWinsockError (*((int *) optval));\r
213   else if (SOCKET_ERROR == ret)\r
214     SetErrnoFromWinsockError (WSAGetLastError ());\r
215 #endif\r
216   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
217 }\r
218 \r
219 /**\r
220  * Listen on a socket\r
221  * @param desc socket\r
222  * @param backlog length of the listen queue\r
223  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
224  */\r
225 int\r
226 GNUNET_NETWORK_socket_listen (const struct GNUNET_NETWORK_Handle *desc,\r
227                               int backlog)\r
228 {\r
229   int ret;\r
230 \r
231   ret = listen (desc->fd, backlog);\r
232 #ifdef MINGW\r
233   if (SOCKET_ERROR == ret)\r
234     SetErrnoFromWinsockError (WSAGetLastError ());\r
235 #endif\r
236 \r
237   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
238 }\r
239 \r
240 /**\r
241  * Read data from a connected socket\r
242  * @param desc socket\r
243  * @param buffer buffer\r
244  * @param length length of buffer\r
245  * @param flags type of message reception\r
246  */\r
247 ssize_t\r
248 GNUNET_NETWORK_socket_recv (const struct GNUNET_NETWORK_Handle * desc,\r
249                             void *buffer, size_t length, int flags)\r
250 {\r
251   int ret;\r
252 \r
253   ret = recv (desc->fd, buffer, length, flags);\r
254 #ifdef MINGW\r
255   if (SOCKET_ERROR == ret)\r
256     SetErrnoFromWinsockError (WSAGetLastError ());\r
257 #endif\r
258 \r
259   return ret;\r
260 }\r
261 \r
262 /**\r
263  * Send data\r
264  * @param desc socket\r
265  * @param buffer data to send\r
266  * @param length size of the buffer\r
267  * @param flags type of message transmission\r
268  * @return number of bytes sent, GNUNET_SYSERR on error\r
269  */\r
270 ssize_t\r
271 GNUNET_NETWORK_socket_send (const struct GNUNET_NETWORK_Handle * desc,\r
272                             const void *buffer, size_t length, int flags)\r
273 {\r
274   int ret;\r
275 \r
276   ret = send (desc->fd, buffer, length, flags);\r
277 #ifdef MINGW\r
278   if (SOCKET_ERROR == ret)\r
279     SetErrnoFromWinsockError (WSAGetLastError ());\r
280 #endif\r
281 \r
282   return ret;\r
283 }\r
284 \r
285 /**\r
286  * Send data\r
287  * @param desc socket\r
288  * @param message data to send\r
289  * @param length size of the data\r
290  * @param flags type of message transmission\r
291  * @param dest_addr destination address\r
292  * @param dest_len length of address\r
293  * @return number of bytes sent, GNUNET_SYSERR on error\r
294  */\r
295 ssize_t\r
296 GNUNET_NETWORK_socket_sendto (const struct GNUNET_NETWORK_Handle * desc,\r
297                               const void *message, size_t length, int flags,\r
298                               const struct sockaddr * dest_addr,\r
299                               socklen_t dest_len)\r
300 {\r
301   int ret;\r
302 \r
303   ret = sendto (desc->fd, message, length, flags, dest_addr, dest_len);\r
304 #ifdef MINGW\r
305   if (SOCKET_ERROR == ret)\r
306     SetErrnoFromWinsockError (WSAGetLastError ());\r
307 #endif\r
308 \r
309   return ret;\r
310 }\r
311 \r
312 /**\r
313  * Set socket option\r
314  * @param fd socket\r
315  * @param level protocol level of the option\r
316  * @param option_name option identifier\r
317  * @param option_value value to set\r
318  * @param option_len size of option_value\r
319  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
320  */\r
321 int\r
322 GNUNET_NETWORK_socket_setsockopt (struct GNUNET_NETWORK_Handle *fd,\r
323                                   int level, int option_name,\r
324                                   const void *option_value,\r
325                                   socklen_t option_len)\r
326 {\r
327   int ret;\r
328 \r
329   ret = setsockopt (fd->fd, level, option_name, option_value, option_len);\r
330 #ifdef MINGW\r
331   if (SOCKET_ERROR == ret)\r
332     SetErrnoFromWinsockError (WSAGetLastError ());\r
333 #endif\r
334 \r
335   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
336 }\r
337 \r
338 /**\r
339  * Create a new socket\r
340  * @param domain domain of the socket\r
341  * @param type socket type\r
342  * @param protocol network protocol\r
343  * @return new socket, NULL on error\r
344  */\r
345 struct GNUNET_NETWORK_Handle *\r
346 GNUNET_NETWORK_socket_socket (int domain, int type, int protocol)\r
347 {\r
348   struct GNUNET_NETWORK_Handle *ret;\r
349 \r
350   ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle));\r
351   ret->fd = socket (domain, type, protocol);\r
352 #ifdef MINGW\r
353   if (INVALID_SOCKET == ret->fd)\r
354     SetErrnoFromWinsockError (WSAGetLastError ());\r
355 #endif\r
356 \r
357   if (ret->fd < 0)\r
358     {\r
359       GNUNET_free (ret);\r
360       ret = NULL;\r
361     }\r
362 \r
363   return ret;\r
364 }\r
365 \r
366 /**\r
367  * Shut down socket operations\r
368  * @param desc socket\r
369  * @param how type of shutdown\r
370  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
371  */\r
372 int\r
373 GNUNET_NETWORK_socket_shutdown (struct GNUNET_NETWORK_Handle *desc,\r
374                                 int how)\r
375 {\r
376   int ret;\r
377 \r
378   ret = shutdown (desc->fd, how);\r
379 #ifdef MINGW\r
380   if (ret != 0)\r
381     SetErrnoFromWinsockError (WSAGetLastError ());\r
382 #endif\r
383 \r
384   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
385 }\r
386 \r
387 /**\r
388  * Make a non-inheritable to child processes\r
389  * @param socket\r
390  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
391  * @warning Not implemented on Windows\r
392  */\r
393 int\r
394 GNUNET_NETWORK_socket_set_inheritable (const struct GNUNET_NETWORK_Handle\r
395                                        *desc)\r
396 {\r
397 #ifdef MINGW\r
398   errno = ENOSYS;\r
399   return GNUNET_SYSERR;\r
400 #else\r
401   return fcntl (desc->fd, F_SETFD,\r
402                 fcntl (desc->fd,\r
403                        F_GETFD) | FD_CLOEXEC) ==\r
404     0 ? GNUNET_OK : GNUNET_SYSERR;\r
405 #endif\r
406 }\r
407 \r
408 \r
409 /**\r
410  * Reset FD set\r
411  * @param fds fd set\r
412  */\r
413 void\r
414 GNUNET_NETWORK_fdset_zero (struct GNUNET_NETWORK_FDSet *fds)\r
415 {\r
416   FD_ZERO (&fds->sds);\r
417   fds->nsds = 0;\r
418 #ifdef MINGW\r
419   GNUNET_CONTAINER_slist_clear (fds->handles);\r
420 #endif\r
421 }\r
422 \r
423 \r
424 /**\r
425  * Add a socket to the FD set\r
426  * @param fds fd set\r
427  * @param desc socket to add\r
428  */\r
429 void\r
430 GNUNET_NETWORK_fdset_set (struct GNUNET_NETWORK_FDSet *fds,\r
431                           const struct GNUNET_NETWORK_Handle *desc)\r
432 {\r
433   FD_SET (desc->fd, &fds->sds);\r
434 \r
435   if (desc->fd + 1 > fds->nsds)\r
436     fds->nsds = desc->fd + 1;\r
437 }\r
438 \r
439 \r
440 /**\r
441  * Check whether a socket is part of the fd set\r
442  * @param fds fd set\r
443  * @param desc socket\r
444  * @return 0 if the FD is not set\r
445  */\r
446 int\r
447 GNUNET_NETWORK_fdset_isset (const struct GNUNET_NETWORK_FDSet *fds,\r
448                             const struct GNUNET_NETWORK_Handle *desc)\r
449 {\r
450   return FD_ISSET (desc->fd, &fds->sds);\r
451 }\r
452 \r
453 \r
454 /**\r
455  * Add one fd set to another\r
456  * @param dst the fd set to add to\r
457  * @param src the fd set to add from\r
458  */\r
459 void\r
460 GNUNET_NETWORK_fdset_add (struct GNUNET_NETWORK_FDSet *dst,\r
461                           const struct GNUNET_NETWORK_FDSet *src)\r
462 {\r
463   int nfds;\r
464 \r
465   for (nfds = src->nsds; nfds > 0; nfds--)\r
466     if (FD_ISSET (nfds, &src->sds))\r
467       {\r
468         FD_SET (nfds, &dst->sds);\r
469         if (nfds + 1 > dst->nsds)\r
470           dst->nsds = nfds + 1;\r
471       }\r
472 }\r
473 \r
474 \r
475 /**\r
476  * Copy one fd set to another\r
477  * @param to destination\r
478  * @param from source\r
479  */\r
480 void\r
481 GNUNET_NETWORK_fdset_copy (struct GNUNET_NETWORK_FDSet *to,\r
482                            const struct GNUNET_NETWORK_FDSet *from)\r
483 {\r
484   FD_COPY (&from->sds, &to->sds);\r
485   to->nsds = from->nsds;\r
486 #ifdef MINGW\r
487   struct GNUNET_CONTAINER_SList_Iterator *iter;\r
488 \r
489 \r
490   GNUNET_CONTAINER_slist_clear (to->handles);\r
491 \r
492   for (iter = GNUNET_CONTAINER_slist_begin (from->handles);\r
493       GNUNET_CONTAINER_slist_end (iter); GNUNET_CONTAINER_slist_next (iter))\r
494     {\r
495       void *handle;\r
496       size_t len;\r
497 \r
498       handle = GNUNET_CONTAINER_slist_get (iter, &len);\r
499       GNUNET_CONTAINER_slist_add (to->handles, GNUNET_MEM_DISP_TRANSIENT, handle, len);\r
500     }\r
501 #endif\r
502 }\r
503 \r
504 \r
505 /**\r
506  * Copy a native fd set\r
507  * @param to destination\r
508  * @param from native source set\r
509  * @param nfds the biggest socket number in from + 1\r
510  */\r
511 void\r
512 GNUNET_NETWORK_fdset_copy_native (struct GNUNET_NETWORK_FDSet *to,\r
513                                   const fd_set * from, int nfds)\r
514 {\r
515   FD_COPY (from, &to->sds);\r
516   to->nsds = nfds;\r
517 }\r
518 \r
519 \r
520 /**\r
521  * Add a file handle to the fd set\r
522  * @param fds fd set\r
523  * @param h the file handle to add\r
524  */\r
525 void\r
526 GNUNET_NETWORK_fdset_handle_set (struct GNUNET_NETWORK_FDSet *fds,\r
527                                  const struct GNUNET_DISK_FileHandle *h)\r
528 {\r
529 #ifdef MINGW\r
530   HANDLE hw;\r
531 \r
532   GNUNET_internal_disk_file_handle (h, &hw, sizeof (HANDLE));\r
533   GNUNET_CONTAINER_slist_add (fds->handles, GNUNET_NO, &hw, sizeof (HANDLE));\r
534 #else\r
535   int fd;\r
536 \r
537   GNUNET_internal_disk_file_handle (h, &fd, sizeof (int));\r
538   FD_SET (fd, &fds->sds);\r
539   if (fd + 1 > fds->nsds)\r
540     fds->nsds = fd + 1;\r
541 #endif\r
542 }\r
543 \r
544 /**\r
545  * Check if a file handle is part of an fd set\r
546  * @param fds fd set\r
547  * @param h file handle\r
548  * @return GNUNET_YES if the file handle is part of the set\r
549  */\r
550 int\r
551 GNUNET_NETWORK_fdset_handle_isset (const struct GNUNET_NETWORK_FDSet *fds,\r
552                                    const struct GNUNET_DISK_FileHandle *h)\r
553 {\r
554 #ifdef MINGW\r
555   return GNUNET_CONTAINER_slist_contains (fds->handles, h->h, sizeof (HANDLE));\r
556 #else\r
557   return FD_ISSET (h->fd, &fds->sds);\r
558 #endif\r
559 }\r
560 \r
561 /**\r
562  * Checks if two fd sets overlap\r
563  * @param fds1 first fd set\r
564  * @param fds2 second fd set\r
565  * @return GNUNET_YES if they do overlap, GNUNET_NO otherwise\r
566  */\r
567 int\r
568 GNUNET_NETWORK_fdset_overlap (const struct GNUNET_NETWORK_FDSet *fds1,\r
569                               const struct GNUNET_NETWORK_FDSet *fds2)\r
570 {\r
571   int nfds;\r
572 \r
573   nfds = fds1->nsds;\r
574   if (nfds < fds2->nsds)\r
575     nfds = fds2->nsds;\r
576 \r
577   for (; nfds >= 0; nfds--)\r
578     if (FD_ISSET (nfds, &fds1->sds) && FD_ISSET (nfds, &fds2->sds))\r
579       return GNUNET_YES;\r
580 \r
581   return GNUNET_NO;\r
582 }\r
583 \r
584 /**\r
585  * Creates an fd set\r
586  * @return a new fd set\r
587  */\r
588 struct GNUNET_NETWORK_FDSet *\r
589 GNUNET_NETWORK_fdset_create ()\r
590 {\r
591   struct GNUNET_NETWORK_FDSet *fds;\r
592 \r
593   fds = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_FDSet));\r
594 #ifdef MINGW\r
595   fds->handles = NULL;\r
596 #endif\r
597   GNUNET_NETWORK_fdset_zero (fds);\r
598   return fds;\r
599 }\r
600 \r
601 \r
602 /**\r
603  * Releases the associated memory of an fd set\r
604  * @param fds fd set\r
605  */\r
606 void\r
607 GNUNET_NETWORK_fdset_destroy (struct GNUNET_NETWORK_FDSet *fds)\r
608 {\r
609 #ifdef MINGW\r
610   GNUNET_CONTAINER_slist_destroy (fds->handles);\r
611 #endif\r
612   GNUNET_free (fds);\r
613 }\r
614 \r
615 \r
616 /**\r
617  * Check if sockets meet certain conditions\r
618  * @param rfds set of sockets to be checked for readability\r
619  * @param wfds set of sockets to be checked for writability\r
620  * @param efds set of sockets to be checked for exceptions\r
621  * @param timeout relative value when to return\r
622  * @return number of selected sockets, GNUNET_SYSERR on error\r
623  */\r
624 int\r
625 GNUNET_NETWORK_socket_select (struct GNUNET_NETWORK_FDSet *rfds,\r
626                               struct GNUNET_NETWORK_FDSet *wfds,\r
627                               struct GNUNET_NETWORK_FDSet *efds,\r
628                               const struct GNUNET_TIME_Relative timeout)\r
629 {\r
630   int nfds;\r
631 \r
632   nfds = 0;\r
633   if (NULL != rfds)\r
634     nfds = rfds->nsds;\r
635   if (NULL != wfds)\r
636     nfds = GNUNET_MAX (nfds, wfds->nsds);\r
637   if (NULL != efds)\r
638     nfds = GNUNET_MAX (nfds, efds->nsds);\r
639 \r
640 #ifndef MINGW\r
641   struct timeval tv;\r
642 \r
643   tv.tv_sec = timeout.value / GNUNET_TIME_UNIT_SECONDS.value;\r
644   tv.tv_usec = 1000 * (timeout.value - (tv.tv_sec * GNUNET_TIME_UNIT_SECONDS.value));\r
645   if ( (nfds == 0) &&\r
646        (timeout.value == GNUNET_TIME_UNIT_FOREVER_REL.value) )\r
647     {\r
648       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,\r
649                   _("Fatal internal logic error, process hangs in `%s' (abort with CTRL-C)!\n"),\r
650                   "select");\r
651       GNUNET_break (0);\r
652     }\r
653   return select (nfds + 1, \r
654                  (rfds != NULL) ? &rfds->sds : NULL, \r
655                  (wfds != NULL) ? &wfds->sds : NULL,\r
656                  (efds != NULL) ? &efds->sds : NULL, \r
657                  (timeout.value == GNUNET_TIME_UNIT_FOREVER_REL.value) \r
658                  ? NULL \r
659                  : &tv);\r
660 #else\r
661   DWORD limit;\r
662   fd_set sock_read, sock_write, sock_except;\r
663   fd_set aread, awrite, aexcept;\r
664   int i;\r
665   struct timeval tvslice;\r
666   int retcode;\r
667   DWORD ms_total;\r
668 \r
669 #define SAFE_FD_ISSET(fd, set)  (set != NULL && FD_ISSET(fd, set))\r
670 \r
671   /* calculate how long we need to wait in milliseconds */\r
672   if (timeout.value == GNUNET_TIME_UNIT_FOREVER_REL.value)\r
673     ms_total = INFINITE;\r
674   else\r
675     ms_total = timeout.value / GNUNET_TIME_UNIT_MILLISECONDS.value;\r
676 \r
677   /* select() may be used as a portable way to sleep */\r
678   if (!(rfds || wfds || efds))\r
679     {\r
680       Sleep (ms_total);\r
681       return 0;\r
682     }\r
683 \r
684   if (rfds)\r
685     sock_read = rfds->sds;\r
686   else\r
687     FD_ZERO(&sock_read);\r
688 \r
689   if (wfds)\r
690     sock_write = wfds->sds;\r
691   else\r
692     FD_ZERO(&sock_write);\r
693 \r
694   if (efds)\r
695     sock_except = efds->sds;\r
696   else\r
697     FD_ZERO(&sock_except);\r
698 \r
699   /* multiplex between winsock select() and waiting on the handles */\r
700 \r
701   FD_ZERO (&aread);\r
702   FD_ZERO (&awrite);\r
703   FD_ZERO (&aexcept);\r
704 \r
705   limit = GetTickCount () + ms_total;\r
706   do\r
707     {\r
708       retcode = 0;\r
709 \r
710       if (nfds > 0)\r
711         {\r
712           /* overwrite the zero'd sets here; the select call\r
713            * will clear those that are not active */\r
714 \r
715           FD_COPY (&sock_read, &aread);\r
716           FD_COPY (&sock_write, &awrite);\r
717           FD_COPY (&sock_except, &aexcept);\r
718 \r
719           tvslice.tv_sec = 0;\r
720           tvslice.tv_usec = 100000;\r
721 \r
722           if ((retcode =\r
723                select (nfds + 1, &aread, &awrite, &aexcept,\r
724                        &tvslice)) == SOCKET_ERROR)\r
725             {\r
726               SetErrnoFromWinsockError (WSAGetLastError ());\r
727               if (errno == ENOTSOCK)\r
728                 errno = EBADF;\r
729 \r
730 #if DEBUG_SOCK\r
731             GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");\r
732 #endif\r
733 \r
734               goto select_loop_end;\r
735             }\r
736         }\r
737 \r
738       /* Poll read pipes */\r
739       if (rfds)\r
740         {\r
741           struct GNUNET_CONTAINER_SList_Iterator *i;\r
742           int on_next;\r
743 \r
744           on_next = GNUNET_NO;\r
745           for (i = GNUNET_CONTAINER_slist_begin (rfds->handles);\r
746                     GNUNET_CONTAINER_slist_end (i) != GNUNET_YES;\r
747                     on_next || GNUNET_CONTAINER_slist_next (i))\r
748             {\r
749               HANDLE h;\r
750               DWORD dwBytes;\r
751 \r
752               h = *(HANDLE *) GNUNET_CONTAINER_slist_get (i, NULL);\r
753               on_next = GNUNET_NO;\r
754 \r
755               if (!PeekNamedPipe (h, NULL, 0, NULL, &dwBytes, NULL))\r
756                 {\r
757                   GNUNET_CONTAINER_slist_erase (i);\r
758                   on_next = GNUNET_YES;\r
759 \r
760                   retcode = -1;\r
761                   SetErrnoFromWinError (GetLastError ());\r
762 #if DEBUG_SOCK\r
763                   GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "PeekNamedPipe");\r
764 #endif\r
765                   goto select_loop_end;\r
766                 }\r
767               else if (dwBytes)\r
768                 {\r
769                   retcode++;\r
770                 }\r
771               else\r
772                 {\r
773                   GNUNET_CONTAINER_slist_erase (i);\r
774                   on_next = GNUNET_YES;\r
775                 }\r
776             }\r
777         }\r
778 \r
779       /* Poll for faulty pipes */\r
780       if (efds)\r
781         {\r
782           struct GNUNET_CONTAINER_SList_Iterator *i;\r
783           int on_next;\r
784 \r
785           on_next = GNUNET_NO;\r
786           for (i = GNUNET_CONTAINER_slist_begin (efds->handles);\r
787                     GNUNET_CONTAINER_slist_end (i) != GNUNET_YES;\r
788                     on_next || GNUNET_CONTAINER_slist_next (i))\r
789             {\r
790               HANDLE h;\r
791               DWORD dwBytes;\r
792 \r
793               h = *(HANDLE *) GNUNET_CONTAINER_slist_get (i, NULL);\r
794 \r
795               if (PeekNamedPipe (h, NULL, 0, NULL, &dwBytes, NULL))\r
796                 {\r
797                   GNUNET_CONTAINER_slist_erase (i);\r
798                   on_next = GNUNET_YES;\r
799 \r
800                   retcode++;\r
801                 }\r
802               else\r
803                 on_next = GNUNET_NO;\r
804             }\r
805         }\r
806 \r
807       /* FIXME */\r
808       if (wfds)\r
809         GNUNET_assert (GNUNET_CONTAINER_slist_count (wfds->handles) == 0);\r
810 \r
811       /* Check for closed sockets */\r
812       for (i = 0; i < nfds; i++)\r
813         {\r
814           if (SAFE_FD_ISSET (i, &sock_read))\r
815             {\r
816               struct sockaddr addr;\r
817               int len;\r
818 \r
819               if (getpeername (i, &addr, &len) == SOCKET_ERROR)\r
820                 {\r
821                   int err, len;\r
822 \r
823                   len = sizeof (err);\r
824                   if (getsockopt\r
825                       (i, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == 0\r
826                       && err == WSAENOTCONN)\r
827                     {\r
828                       if (!SAFE_FD_ISSET (i, &aread))\r
829                         {\r
830                           FD_SET (i, &aread);\r
831                           retcode++;\r
832                         }\r
833                     }\r
834                 }\r
835             }\r
836         }\r
837 \r
838     select_loop_end:;\r
839     }\r
840   while (retcode == 0 && (ms_total == INFINITE || GetTickCount () < limit));\r
841 \r
842   if (retcode != -1)\r
843     {\r
844       if (rfds)\r
845         {\r
846           GNUNET_NETWORK_fdset_zero (rfds);\r
847           GNUNET_NETWORK_fdset_copy_native (rfds, &aread, retcode);\r
848         }\r
849 \r
850       if (wfds)\r
851         {\r
852           GNUNET_NETWORK_fdset_zero (wfds);\r
853           GNUNET_NETWORK_fdset_copy_native (wfds, &awrite, retcode);\r
854         }\r
855 \r
856       if (efds)\r
857         {\r
858           GNUNET_NETWORK_fdset_zero (efds);\r
859           GNUNET_NETWORK_fdset_copy_native (efds, &aexcept, retcode);\r
860         }\r
861     }\r
862 \r
863   return retcode;\r
864 #endif\r
865 }\r
866 \r
867 \r
868 /* end of network.c */\r