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