91d4066b2a2429c02afadff61a166279da380f15
[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   int eno;\r
152 \r
153 #ifdef MINGW\r
154   ret = closesocket (desc->fd);\r
155   SetErrnoFromWinsockError (WSAGetLastError ());  \r
156 #else\r
157   ret = close (desc->fd);\r
158 #endif  \r
159   eno = errno;\r
160   GNUNET_free (desc);\r
161   errno = eno;\r
162   return (ret == 0) ? GNUNET_OK : GNUNET_SYSERR;\r
163 }\r
164 \r
165 /**\r
166  * Connect a socket\r
167  * @param desc socket\r
168  * @param address peer address\r
169  * @param address_len length of address\r
170  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
171  */\r
172 int\r
173 GNUNET_NETWORK_socket_connect (const struct GNUNET_NETWORK_Handle *desc,\r
174                                const struct sockaddr *address,\r
175                                socklen_t address_len)\r
176 {\r
177   int ret;\r
178 \r
179   ret = connect (desc->fd, address, address_len);\r
180 #ifdef MINGW\r
181   if (SOCKET_ERROR == ret)\r
182     {\r
183       SetErrnoFromWinsockError (WSAGetLastError ());\r
184       if (errno == EWOULDBLOCK)\r
185         errno = EINPROGRESS;\r
186     }\r
187 #endif\r
188   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
189 }\r
190 \r
191 /**\r
192  * Get socket options\r
193  * @param desc socket\r
194  * @param level protocol level of the option\r
195  * @param optname identifier of the option\r
196  * @param optval options\r
197  * @param optlen length of optval\r
198  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
199  */\r
200 int\r
201 GNUNET_NETWORK_socket_getsockopt (const struct GNUNET_NETWORK_Handle *desc,\r
202                                   int level, int optname, void *optval,\r
203                                   socklen_t * optlen)\r
204 {\r
205   int ret;\r
206 \r
207   ret = getsockopt (desc->fd, level, optname, optval, optlen);\r
208 #ifdef MINGW\r
209   if (ret == 0 && level == SOL_SOCKET && optname == SO_ERROR)\r
210     *((int *) optval) = GetErrnoFromWinsockError (*((int *) optval));\r
211   else if (SOCKET_ERROR == ret)\r
212     SetErrnoFromWinsockError (WSAGetLastError ());\r
213 #endif\r
214   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
215 }\r
216 \r
217 /**\r
218  * Listen on a socket\r
219  * @param desc socket\r
220  * @param backlog length of the listen queue\r
221  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
222  */\r
223 int\r
224 GNUNET_NETWORK_socket_listen (const struct GNUNET_NETWORK_Handle *desc,\r
225                               int backlog)\r
226 {\r
227   int ret;\r
228 \r
229   ret = listen (desc->fd, backlog);\r
230 #ifdef MINGW\r
231   if (SOCKET_ERROR == ret)\r
232     SetErrnoFromWinsockError (WSAGetLastError ());\r
233 #endif\r
234 \r
235   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
236 }\r
237 \r
238 /**\r
239  * Read data from a connected socket\r
240  * @param desc socket\r
241  * @param buffer buffer\r
242  * @param length length of buffer\r
243  * @param flags type of message reception\r
244  */\r
245 ssize_t\r
246 GNUNET_NETWORK_socket_recv (const struct GNUNET_NETWORK_Handle * desc,\r
247                             void *buffer, size_t length, int flags)\r
248 {\r
249   int ret;\r
250 \r
251   ret = recv (desc->fd, buffer, length, flags);\r
252 #ifdef MINGW\r
253   if (SOCKET_ERROR == ret)\r
254     SetErrnoFromWinsockError (WSAGetLastError ());\r
255 #endif\r
256 \r
257   return ret;\r
258 }\r
259 \r
260 /**\r
261  * Send data\r
262  * @param desc socket\r
263  * @param buffer data to send\r
264  * @param length size of the buffer\r
265  * @param flags type of message transmission\r
266  * @return number of bytes sent, GNUNET_SYSERR on error\r
267  */\r
268 ssize_t\r
269 GNUNET_NETWORK_socket_send (const struct GNUNET_NETWORK_Handle * desc,\r
270                             const void *buffer, size_t length, int flags)\r
271 {\r
272   int ret;\r
273 \r
274   ret = send (desc->fd, buffer, length, flags);\r
275 #ifdef MINGW\r
276   if (SOCKET_ERROR == ret)\r
277     SetErrnoFromWinsockError (WSAGetLastError ());\r
278 #endif\r
279 \r
280   return ret;\r
281 }\r
282 \r
283 /**\r
284  * Send data\r
285  * @param desc socket\r
286  * @param message data to send\r
287  * @param length size of the data\r
288  * @param flags type of message transmission\r
289  * @param dest_addr destination address\r
290  * @param dest_len length of address\r
291  * @return number of bytes sent, GNUNET_SYSERR on error\r
292  */\r
293 ssize_t\r
294 GNUNET_NETWORK_socket_sendto (const struct GNUNET_NETWORK_Handle * desc,\r
295                               const void *message, size_t length, int flags,\r
296                               const struct sockaddr * dest_addr,\r
297                               socklen_t dest_len)\r
298 {\r
299   int ret;\r
300 \r
301   ret = sendto (desc->fd, message, length, flags, dest_addr, dest_len);\r
302 #ifdef MINGW\r
303   if (SOCKET_ERROR == ret)\r
304     SetErrnoFromWinsockError (WSAGetLastError ());\r
305 #endif\r
306 \r
307   return ret;\r
308 }\r
309 \r
310 /**\r
311  * Set socket option\r
312  * @param fd socket\r
313  * @param level protocol level of the option\r
314  * @param option_name option identifier\r
315  * @param option_value value to set\r
316  * @param option_len size of option_value\r
317  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
318  */\r
319 int\r
320 GNUNET_NETWORK_socket_setsockopt (struct GNUNET_NETWORK_Handle *fd,\r
321                                   int level, int option_name,\r
322                                   const void *option_value,\r
323                                   socklen_t option_len)\r
324 {\r
325   int ret;\r
326 \r
327   ret = setsockopt (fd->fd, level, option_name, option_value, option_len);\r
328 #ifdef MINGW\r
329   if (SOCKET_ERROR == ret)\r
330     SetErrnoFromWinsockError (WSAGetLastError ());\r
331 #endif\r
332 \r
333   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
334 }\r
335 \r
336 /**\r
337  * Create a new socket\r
338  * @param domain domain of the socket\r
339  * @param type socket type\r
340  * @param protocol network protocol\r
341  * @return new socket, NULL on error\r
342  */\r
343 struct GNUNET_NETWORK_Handle *\r
344 GNUNET_NETWORK_socket_socket (int domain, int type, int protocol)\r
345 {\r
346   struct GNUNET_NETWORK_Handle *ret;\r
347 \r
348   ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle));\r
349   ret->fd = socket (domain, type, protocol);\r
350 #ifdef MINGW\r
351   if (INVALID_SOCKET == ret->fd)\r
352     SetErrnoFromWinsockError (WSAGetLastError ());\r
353 #endif\r
354 \r
355   if (ret->fd < 0)\r
356     {\r
357       GNUNET_free (ret);\r
358       ret = NULL;\r
359     }\r
360 \r
361   return ret;\r
362 }\r
363 \r
364 /**\r
365  * Shut down socket operations\r
366  * @param desc socket\r
367  * @param how type of shutdown\r
368  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
369  */\r
370 int\r
371 GNUNET_NETWORK_socket_shutdown (struct GNUNET_NETWORK_Handle *desc,\r
372                                 int how)\r
373 {\r
374   int ret;\r
375 \r
376   ret = shutdown (desc->fd, how);\r
377 #ifdef MINGW\r
378   if (ret != 0)\r
379     SetErrnoFromWinsockError (WSAGetLastError ());\r
380 #endif\r
381 \r
382   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
383 }\r
384 \r
385 /**\r
386  * Make a non-inheritable to child processes\r
387  *\r
388  * @param h the socket to make non-inheritable\r
389  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
390  * @warning Not implemented on Windows\r
391  */\r
392 int\r
393 GNUNET_NETWORK_socket_set_inheritable (const struct GNUNET_NETWORK_Handle\r
394                                        *h)\r
395 {\r
396 #ifdef MINGW\r
397   errno = ENOSYS;\r
398   return GNUNET_SYSERR;\r
399 #else\r
400   int i;\r
401 \r
402   i = fcntl (h->fd, F_GETFD);\r
403   if (i == (i | FD_CLOEXEC))\r
404     return GNUNET_OK;\r
405   return (fcntl (h->fd, F_SETFD, i | FD_CLOEXEC) == 0)\r
406     ? GNUNET_OK : GNUNET_SYSERR;\r
407 #endif\r
408 }\r
409 \r
410 \r
411 /**\r
412  * Reset FD set\r
413  * @param fds fd set\r
414  */\r
415 void\r
416 GNUNET_NETWORK_fdset_zero (struct GNUNET_NETWORK_FDSet *fds)\r
417 {\r
418   FD_ZERO (&fds->sds);\r
419   fds->nsds = 0;\r
420 #ifdef MINGW\r
421   GNUNET_CONTAINER_slist_clear (fds->handles);\r
422 #endif\r
423 }\r
424 \r
425 \r
426 /**\r
427  * Add a socket to the FD set\r
428  * @param fds fd set\r
429  * @param desc socket to add\r
430  */\r
431 void\r
432 GNUNET_NETWORK_fdset_set (struct GNUNET_NETWORK_FDSet *fds,\r
433                           const struct GNUNET_NETWORK_Handle *desc)\r
434 {\r
435   FD_SET (desc->fd, &fds->sds);\r
436 \r
437   if (desc->fd + 1 > fds->nsds)\r
438     fds->nsds = desc->fd + 1;\r
439 }\r
440 \r
441 \r
442 /**\r
443  * Check whether a socket is part of the fd set\r
444  * @param fds fd set\r
445  * @param desc socket\r
446  * @return 0 if the FD is not set\r
447  */\r
448 int\r
449 GNUNET_NETWORK_fdset_isset (const struct GNUNET_NETWORK_FDSet *fds,\r
450                             const struct GNUNET_NETWORK_Handle *desc)\r
451 {\r
452   return FD_ISSET (desc->fd, &fds->sds);\r
453 }\r
454 \r
455 \r
456 /**\r
457  * Add one fd set to another\r
458  * @param dst the fd set to add to\r
459  * @param src the fd set to add from\r
460  */\r
461 void\r
462 GNUNET_NETWORK_fdset_add (struct GNUNET_NETWORK_FDSet *dst,\r
463                           const struct GNUNET_NETWORK_FDSet *src)\r
464 {\r
465   int nfds;\r
466 \r
467   for (nfds = src->nsds; nfds > 0; nfds--)\r
468     if (FD_ISSET (nfds, &src->sds))\r
469       {\r
470         FD_SET (nfds, &dst->sds);\r
471         if (nfds + 1 > dst->nsds)\r
472           dst->nsds = nfds + 1;\r
473       }\r
474 }\r
475 \r
476 \r
477 /**\r
478  * Copy one fd set to another\r
479  * @param to destination\r
480  * @param from source\r
481  */\r
482 void\r
483 GNUNET_NETWORK_fdset_copy (struct GNUNET_NETWORK_FDSet *to,\r
484                            const struct GNUNET_NETWORK_FDSet *from)\r
485 {\r
486   FD_COPY (&from->sds, &to->sds);\r
487   to->nsds = from->nsds;\r
488 #ifdef MINGW\r
489   struct GNUNET_CONTAINER_SList_Iterator *iter;\r
490 \r
491 \r
492   GNUNET_CONTAINER_slist_clear (to->handles);\r
493 \r
494   for (iter = GNUNET_CONTAINER_slist_begin (from->handles);\r
495       GNUNET_CONTAINER_slist_end (iter) != GNUNET_YES; GNUNET_CONTAINER_slist_next (iter))\r
496     {\r
497       void *handle;\r
498       size_t len;\r
499 \r
500       handle = GNUNET_CONTAINER_slist_get (iter, &len);\r
501       GNUNET_CONTAINER_slist_add (to->handles, GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, handle, len);\r
502     }\r
503 #endif\r
504 }\r
505 \r
506 \r
507 /**\r
508  * Copy a native fd set\r
509  * @param to destination\r
510  * @param from native source set\r
511  * @param nfds the biggest socket number in from + 1\r
512  */\r
513 void\r
514 GNUNET_NETWORK_fdset_copy_native (struct GNUNET_NETWORK_FDSet *to,\r
515                                   const fd_set * from, int nfds)\r
516 {\r
517   FD_COPY (from, &to->sds);\r
518   to->nsds = nfds;\r
519 }\r
520 \r
521 \r
522 /**\r
523  * Add a file handle to the fd set\r
524  * @param fds fd set\r
525  * @param h the file handle to add\r
526  */\r
527 void\r
528 GNUNET_NETWORK_fdset_handle_set (struct GNUNET_NETWORK_FDSet *fds,\r
529                                  const struct GNUNET_DISK_FileHandle *h)\r
530 {\r
531 #ifdef MINGW\r
532   HANDLE hw;\r
533 \r
534   GNUNET_DISK_internal_file_handle_ (h, &hw, sizeof (HANDLE));\r
535   GNUNET_CONTAINER_slist_add (fds->handles, GNUNET_NO, &hw, sizeof (HANDLE));\r
536 #else\r
537   int fd;\r
538 \r
539   GNUNET_DISK_internal_file_handle_ (h, &fd, sizeof (int));\r
540   FD_SET (fd, &fds->sds);\r
541   if (fd + 1 > fds->nsds)\r
542     fds->nsds = fd + 1;\r
543 #endif\r
544 }\r
545 \r
546 /**\r
547  * Check if a file handle is part of an fd set\r
548  * @param fds fd set\r
549  * @param h file handle\r
550  * @return GNUNET_YES if the file handle is part of the set\r
551  */\r
552 int\r
553 GNUNET_NETWORK_fdset_handle_isset (const struct GNUNET_NETWORK_FDSet *fds,\r
554                                    const struct GNUNET_DISK_FileHandle *h)\r
555 {\r
556 #ifdef MINGW\r
557   return GNUNET_CONTAINER_slist_contains (fds->handles, h->h, sizeof (HANDLE));\r
558 #else\r
559   return FD_ISSET (h->fd, &fds->sds);\r
560 #endif\r
561 }\r
562 \r
563 /**\r
564  * Checks if two fd sets overlap\r
565  * @param fds1 first fd set\r
566  * @param fds2 second fd set\r
567  * @return GNUNET_YES if they do overlap, GNUNET_NO otherwise\r
568  */\r
569 int\r
570 GNUNET_NETWORK_fdset_overlap (const struct GNUNET_NETWORK_FDSet *fds1,\r
571                               const struct GNUNET_NETWORK_FDSet *fds2)\r
572 {\r
573   int nfds;\r
574 \r
575   nfds = fds1->nsds;\r
576   if (nfds < fds2->nsds)\r
577     nfds = fds2->nsds;\r
578 \r
579   for (; nfds >= 0; nfds--)\r
580     if (FD_ISSET (nfds, &fds1->sds) && FD_ISSET (nfds, &fds2->sds))\r
581       return GNUNET_YES;\r
582 \r
583   return GNUNET_NO;\r
584 }\r
585 \r
586 /**\r
587  * Creates an fd set\r
588  * @return a new fd set\r
589  */\r
590 struct GNUNET_NETWORK_FDSet *\r
591 GNUNET_NETWORK_fdset_create ()\r
592 {\r
593   struct GNUNET_NETWORK_FDSet *fds;\r
594 \r
595   fds = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_FDSet));\r
596 #ifdef MINGW\r
597   fds->handles = GNUNET_CONTAINER_slist_create ();\r
598 #endif\r
599   GNUNET_NETWORK_fdset_zero (fds);\r
600   return fds;\r
601 }\r
602 \r
603 \r
604 /**\r
605  * Releases the associated memory of an fd set\r
606  * @param fds fd set\r
607  */\r
608 void\r
609 GNUNET_NETWORK_fdset_destroy (struct GNUNET_NETWORK_FDSet *fds)\r
610 {\r
611 #ifdef MINGW\r
612   GNUNET_CONTAINER_slist_destroy (fds->handles);\r
613 #endif\r
614   GNUNET_free (fds);\r
615 }\r
616 \r
617 \r
618 /**\r
619  * Check if sockets meet certain conditions\r
620  * @param rfds set of sockets to be checked for readability\r
621  * @param wfds set of sockets to be checked for writability\r
622  * @param efds set of sockets to be checked for exceptions\r
623  * @param timeout relative value when to return\r
624  * @return number of selected sockets, GNUNET_SYSERR on error\r
625  */\r
626 int\r
627 GNUNET_NETWORK_socket_select (struct GNUNET_NETWORK_FDSet *rfds,\r
628                               struct GNUNET_NETWORK_FDSet *wfds,\r
629                               struct GNUNET_NETWORK_FDSet *efds,\r
630                               const struct GNUNET_TIME_Relative timeout)\r
631 {\r
632   int nfds;\r
633 \r
634   nfds = 0;\r
635   if (NULL != rfds)\r
636     nfds = rfds->nsds;\r
637   if (NULL != wfds)\r
638     nfds = GNUNET_MAX (nfds, wfds->nsds);\r
639   if (NULL != efds)\r
640     nfds = GNUNET_MAX (nfds, efds->nsds);\r
641 \r
642 #ifndef MINGW\r
643   struct timeval tv;\r
644 \r
645   tv.tv_sec = timeout.value / GNUNET_TIME_UNIT_SECONDS.value;\r
646   tv.tv_usec = 1000 * (timeout.value - (tv.tv_sec * GNUNET_TIME_UNIT_SECONDS.value));\r
647   if ( (nfds == 0) &&\r
648        (timeout.value == GNUNET_TIME_UNIT_FOREVER_REL.value) )\r
649     {\r
650       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,\r
651                   _("Fatal internal logic error, process hangs in `%s' (abort with CTRL-C)!\n"),\r
652                   "select");\r
653       GNUNET_break (0);\r
654     }\r
655   return select (nfds + 1, \r
656                  (rfds != NULL) ? &rfds->sds : NULL, \r
657                  (wfds != NULL) ? &wfds->sds : NULL,\r
658                  (efds != NULL) ? &efds->sds : NULL, \r
659                  (timeout.value == GNUNET_TIME_UNIT_FOREVER_REL.value) \r
660                  ? NULL \r
661                  : &tv);\r
662 #else\r
663   DWORD limit;\r
664   fd_set sock_read, sock_write, sock_except;\r
665   fd_set aread, awrite, aexcept;\r
666   int i;\r
667   struct timeval tvslice;\r
668   int retcode;\r
669   DWORD ms_total;\r
670 \r
671 #define SAFE_FD_ISSET(fd, set)  (set != NULL && FD_ISSET(fd, set))\r
672 \r
673   /* calculate how long we need to wait in milliseconds */\r
674   if (timeout.value == GNUNET_TIME_UNIT_FOREVER_REL.value)\r
675     ms_total = INFINITE;\r
676   else\r
677     ms_total = timeout.value / GNUNET_TIME_UNIT_MILLISECONDS.value;\r
678 \r
679   /* select() may be used as a portable way to sleep */\r
680   if (!(rfds || wfds || efds))\r
681     {\r
682       Sleep (ms_total);\r
683       return 0;\r
684     }\r
685 \r
686   if (rfds)\r
687     sock_read = rfds->sds;\r
688   else\r
689     FD_ZERO(&sock_read);\r
690 \r
691   if (wfds)\r
692     sock_write = wfds->sds;\r
693   else\r
694     FD_ZERO(&sock_write);\r
695 \r
696   if (efds)\r
697     sock_except = efds->sds;\r
698   else\r
699     FD_ZERO(&sock_except);\r
700 \r
701   /* multiplex between winsock select() and waiting on the handles */\r
702 \r
703   FD_ZERO (&aread);\r
704   FD_ZERO (&awrite);\r
705   FD_ZERO (&aexcept);\r
706 \r
707   limit = GetTickCount () + ms_total;\r
708   do\r
709     {\r
710       retcode = 0;\r
711 \r
712       if (nfds > 0)\r
713         {\r
714           /* overwrite the zero'd sets here; the select call\r
715            * will clear those that are not active */\r
716 \r
717           FD_COPY (&sock_read, &aread);\r
718           FD_COPY (&sock_write, &awrite);\r
719           FD_COPY (&sock_except, &aexcept);\r
720 \r
721           tvslice.tv_sec = 0;\r
722           tvslice.tv_usec = 100000;\r
723 \r
724           if ((retcode =\r
725                select (nfds + 1, &aread, &awrite, &aexcept,\r
726                        &tvslice)) == SOCKET_ERROR)\r
727             {\r
728               SetErrnoFromWinsockError (WSAGetLastError ());\r
729               if (errno == ENOTSOCK)\r
730                 errno = EBADF;\r
731 \r
732 #if DEBUG_SOCK\r
733             GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");\r
734 #endif\r
735 \r
736               goto select_loop_end;\r
737             }\r
738         }\r
739 \r
740       /* Poll read pipes */\r
741       if (rfds)\r
742         {\r
743           struct GNUNET_CONTAINER_SList_Iterator *i;\r
744           int on_next;\r
745 \r
746           on_next = GNUNET_NO;\r
747           for (i = GNUNET_CONTAINER_slist_begin (rfds->handles);\r
748                     GNUNET_CONTAINER_slist_end (i) != GNUNET_YES;\r
749                     on_next || GNUNET_CONTAINER_slist_next (i))\r
750             {\r
751               HANDLE h;\r
752               DWORD dwBytes;\r
753 \r
754               h = *(HANDLE *) GNUNET_CONTAINER_slist_get (i, NULL);\r
755               on_next = GNUNET_NO;\r
756 \r
757               if (!PeekNamedPipe (h, NULL, 0, NULL, &dwBytes, NULL))\r
758                 {\r
759                   GNUNET_CONTAINER_slist_erase (i);\r
760                   on_next = GNUNET_YES;\r
761 \r
762                   retcode = -1;\r
763                   SetErrnoFromWinError (GetLastError ());\r
764 #if DEBUG_SOCK\r
765                   GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "PeekNamedPipe");\r
766 #endif\r
767                   goto select_loop_end;\r
768                 }\r
769               else if (dwBytes)\r
770                 {\r
771                   retcode++;\r
772                 }\r
773               else\r
774                 {\r
775                   GNUNET_CONTAINER_slist_erase (i);\r
776                   on_next = GNUNET_YES;\r
777                 }\r
778             }\r
779         }\r
780 \r
781       /* Poll for faulty pipes */\r
782       if (efds)\r
783         {\r
784           struct GNUNET_CONTAINER_SList_Iterator *i;\r
785           int on_next;\r
786 \r
787           on_next = GNUNET_NO;\r
788           for (i = GNUNET_CONTAINER_slist_begin (efds->handles);\r
789                     GNUNET_CONTAINER_slist_end (i) != GNUNET_YES;\r
790                     on_next || GNUNET_CONTAINER_slist_next (i))\r
791             {\r
792               HANDLE h;\r
793               DWORD dwBytes;\r
794 \r
795               h = *(HANDLE *) GNUNET_CONTAINER_slist_get (i, NULL);\r
796 \r
797               if (PeekNamedPipe (h, NULL, 0, NULL, &dwBytes, NULL))\r
798                 {\r
799                   GNUNET_CONTAINER_slist_erase (i);\r
800                   on_next = GNUNET_YES;\r
801 \r
802                   retcode++;\r
803                 }\r
804               else\r
805                 on_next = GNUNET_NO;\r
806             }\r
807         }\r
808 \r
809       /* FIXME */\r
810       if (wfds)\r
811         GNUNET_assert (GNUNET_CONTAINER_slist_count (wfds->handles) == 0);\r
812 \r
813       /* Check for closed sockets */\r
814       for (i = 0; i < nfds; i++)\r
815         {\r
816           if (SAFE_FD_ISSET (i, &sock_read))\r
817             {\r
818               struct sockaddr addr;\r
819               int len;\r
820 \r
821               if (getpeername (i, &addr, &len) == SOCKET_ERROR)\r
822                 {\r
823                   int err, len;\r
824 \r
825                   len = sizeof (err);\r
826                   if (getsockopt\r
827                       (i, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == 0\r
828                       && err == WSAENOTCONN)\r
829                     {\r
830                       if (!SAFE_FD_ISSET (i, &aread))\r
831                         {\r
832                           FD_SET (i, &aread);\r
833                           retcode++;\r
834                         }\r
835                     }\r
836                 }\r
837             }\r
838         }\r
839 \r
840     select_loop_end:;\r
841     }\r
842   while (retcode == 0 && (ms_total == INFINITE || GetTickCount () < limit));\r
843 \r
844   if (retcode != -1)\r
845     {\r
846       if (rfds)\r
847         {\r
848           GNUNET_NETWORK_fdset_zero (rfds);\r
849           GNUNET_NETWORK_fdset_copy_native (rfds, &aread, retcode);\r
850         }\r
851 \r
852       if (wfds)\r
853         {\r
854           GNUNET_NETWORK_fdset_zero (wfds);\r
855           GNUNET_NETWORK_fdset_copy_native (wfds, &awrite, retcode);\r
856         }\r
857 \r
858       if (efds)\r
859         {\r
860           GNUNET_NETWORK_fdset_zero (efds);\r
861           GNUNET_NETWORK_fdset_copy_native (efds, &aexcept, retcode);\r
862         }\r
863     }\r
864 \r
865   return retcode;\r
866 #endif\r
867 }\r
868 \r
869 \r
870 /* end of network.c */\r