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