7039ff54aa8b8ac3feb95af4b4c3bdc62eb22e8a
[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 #ifndef INVALID_SOCKET\r
35 #define INVALID_SOCKET -1\r
36 #endif\r
37 \r
38 struct GNUNET_NETWORK_Handle\r
39 {\r
40   int fd;\r
41 };\r
42 \r
43 struct GNUNET_NETWORK_FDSet\r
44 {\r
45   /* socket descriptors */\r
46   int nsds;\r
47   fd_set sds;\r
48 #ifdef WINDOWS\r
49   /* handles */\r
50   struct GNUNET_CONTAINER_SList *handles;\r
51 #endif\r
52 };\r
53 \r
54 #ifndef FD_COPY\r
55 #define FD_COPY(s, d) (memcpy ((d), (s), sizeof (fd_set)))\r
56 #endif\r
57 \r
58 \r
59 \r
60 /**\r
61  * Set if a socket should use blocking or non-blocking IO.\r
62  * @param fd socket\r
63  * @param doBlock blocking mode\r
64  * @return GNUNET_OK on success, GNUNET_SYSERR on error\r
65  */\r
66 static int\r
67 socket_set_blocking (struct GNUNET_NETWORK_Handle *fd,\r
68                      int doBlock)\r
69 {\r
70 #if MINGW\r
71   u_long mode;\r
72   mode = !doBlock;\r
73   if (ioctlsocket (fd->fd, FIONBIO, &mode) == SOCKET_ERROR)\r
74     {\r
75       SetErrnoFromWinsockError (WSAGetLastError ());\r
76       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket");\r
77       return GNUNET_SYSERR;\r
78     }\r
79   return GNUNET_OK;\r
80 \r
81 #else\r
82   /* not MINGW */\r
83   int flags = fcntl (fd->fd, F_GETFL);\r
84   if (flags == -1)\r
85     {\r
86       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl");\r
87       return GNUNET_SYSERR;\r
88     }\r
89   if (doBlock)\r
90     flags &= ~O_NONBLOCK;\r
91   else\r
92     flags |= O_NONBLOCK;\r
93   if (0 != fcntl (fd->fd, F_SETFL, flags))\r
94     {\r
95       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl");\r
96       return GNUNET_SYSERR;\r
97     }\r
98   return GNUNET_OK;\r
99 #endif\r
100 }\r
101 \r
102 \r
103 #ifndef MINGW\r
104 /**\r
105  * Make a socket non-inheritable to child processes\r
106  *\r
107  * @param h the socket to make non-inheritable\r
108  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
109  * @warning Not implemented on Windows\r
110  */\r
111 static int\r
112 socket_set_inheritable (const struct GNUNET_NETWORK_Handle\r
113                                        *h)\r
114 {\r
115   int i;\r
116 \r
117   i = fcntl (h->fd, F_GETFD);\r
118   if (i == (i | FD_CLOEXEC))\r
119     return GNUNET_OK;\r
120   return (fcntl (h->fd, F_SETFD, i | FD_CLOEXEC) == 0)\r
121     ? GNUNET_OK : GNUNET_SYSERR;\r
122 }\r
123 #endif\r
124 \r
125 \r
126 \r
127 #ifdef DARWIN\r
128 /**\r
129  * The MSG_NOSIGNAL equivalent on Mac OS X\r
130  *\r
131  * @param h the socket to make non-delaying\r
132  */\r
133 static void\r
134 socket_set_nosigpipe (const struct GNUNET_NETWORK_Handle\r
135                     *h)\r
136 {\r
137   int value = 1;\r
138   setsockopt (h->fd, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));\r
139 }\r
140 #endif\r
141 \r
142 \r
143 \r
144 /**\r
145  * Disable delays when sending data via the socket.\r
146  * (GNUnet makes sure that messages are as big as\r
147  * possible already).\r
148  *\r
149  * @param h the socket to make non-delaying\r
150  */\r
151 static void\r
152 socket_set_nodelay (const struct GNUNET_NETWORK_Handle\r
153                     *h)\r
154 {\r
155   int value = 1;\r
156   setsockopt (h->fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value));\r
157 }\r
158 \r
159 \r
160 \r
161 /**\r
162  * accept a new connection on a socket\r
163  *\r
164  * @param desc bound socket\r
165  * @param address address of the connecting peer, may be NULL\r
166  * @param address_len length of address\r
167  * @return client socket\r
168  */\r
169 struct GNUNET_NETWORK_Handle *\r
170 GNUNET_NETWORK_socket_accept (const struct GNUNET_NETWORK_Handle *desc,\r
171                               struct sockaddr *address,\r
172                               socklen_t * address_len)\r
173 {\r
174   struct GNUNET_NETWORK_Handle *ret;\r
175 \r
176   ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle));\r
177   ret->fd = accept (desc->fd, address, address_len);\r
178   if (ret->fd == INVALID_SOCKET)\r
179     {\r
180 #ifdef MINGW\r
181       SetErrnoFromWinsockError (WSAGetLastError ());\r
182 #endif\r
183       GNUNET_free (ret);\r
184       return NULL;\r
185     }\r
186 #ifndef MINGW\r
187   if (ret->fd >= FD_SETSIZE)\r
188     {\r
189       close (desc->fd);\r
190       GNUNET_free (ret);\r
191       errno = EMFILE;\r
192       return NULL;\r
193     }\r
194 #endif\r
195   if (GNUNET_SYSERR == socket_set_blocking (ret, GNUNET_NO))\r
196     {\r
197       /* we might want to treat this one as fatal... */\r
198       GNUNET_break (0);\r
199       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ret));\r
200       return NULL; \r
201     }\r
202 #ifndef MINGW\r
203   if (GNUNET_OK != socket_set_inheritable (ret))\r
204     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,\r
205                          "socket_set_inheritable");\r
206 #endif\r
207 #ifdef DARWIN\r
208   socket_set_nosigpipe (ret);  \r
209 #endif\r
210   socket_set_nodelay (ret);\r
211   return ret;\r
212 }\r
213 \r
214 /**\r
215  * Bind to a connected socket\r
216  * @param desc socket\r
217  * @param address address to be bound\r
218  * @param address_len length of address\r
219  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
220  */\r
221 int\r
222 GNUNET_NETWORK_socket_bind (struct GNUNET_NETWORK_Handle *desc,\r
223                             const struct sockaddr *address,\r
224                             socklen_t address_len)\r
225 {\r
226   int ret;\r
227 \r
228   ret = bind (desc->fd, address, address_len);\r
229 #ifdef MINGW\r
230   if (SOCKET_ERROR == ret)\r
231     SetErrnoFromWinsockError (WSAGetLastError ());\r
232 #endif\r
233   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
234 }\r
235 \r
236 \r
237 /**\r
238  * Close a socket\r
239  * @param desc socket\r
240  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
241  */\r
242 int\r
243 GNUNET_NETWORK_socket_close (struct GNUNET_NETWORK_Handle *desc)\r
244 {\r
245   int ret;\r
246   int eno;\r
247 \r
248 #ifdef MINGW\r
249   ret = closesocket (desc->fd);\r
250   SetErrnoFromWinsockError (WSAGetLastError ());  \r
251 #else\r
252   ret = close (desc->fd);\r
253 #endif  \r
254   eno = errno;\r
255   GNUNET_free (desc);\r
256   errno = eno;\r
257   return (ret == 0) ? GNUNET_OK : GNUNET_SYSERR;\r
258 }\r
259 \r
260 /**\r
261  * Connect a socket\r
262  * @param desc socket\r
263  * @param address peer address\r
264  * @param address_len length of address\r
265  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
266  */\r
267 int\r
268 GNUNET_NETWORK_socket_connect (const struct GNUNET_NETWORK_Handle *desc,\r
269                                const struct sockaddr *address,\r
270                                socklen_t address_len)\r
271 {\r
272   int ret;\r
273 \r
274   ret = connect (desc->fd, address, address_len);\r
275 #ifdef MINGW\r
276   if (SOCKET_ERROR == ret)\r
277     {\r
278       SetErrnoFromWinsockError (WSAGetLastError ());\r
279       if (errno == EWOULDBLOCK)\r
280         errno = EINPROGRESS;\r
281     }\r
282 #endif\r
283   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
284 }\r
285 \r
286 /**\r
287  * Get socket options\r
288  *\r
289  * @param desc socket\r
290  * @param level protocol level of the option\r
291  * @param optname identifier of the option\r
292  * @param optval options\r
293  * @param optlen length of optval\r
294  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
295  */\r
296 int\r
297 GNUNET_NETWORK_socket_getsockopt (const struct GNUNET_NETWORK_Handle *desc,\r
298                                   int level, int optname, void *optval,\r
299                                   socklen_t * optlen)\r
300 {\r
301   int ret;\r
302 \r
303   ret = getsockopt (desc->fd, level, optname, optval, optlen);\r
304 #ifdef MINGW\r
305   if (ret == 0 && level == SOL_SOCKET && optname == SO_ERROR)\r
306     *((int *) optval) = GetErrnoFromWinsockError (*((int *) optval));\r
307   else if (SOCKET_ERROR == ret)\r
308     SetErrnoFromWinsockError (WSAGetLastError ());\r
309 #endif\r
310   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
311 }\r
312 \r
313 /**\r
314  * Listen on a socket\r
315  * @param desc socket\r
316  * @param backlog length of the listen queue\r
317  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
318  */\r
319 int\r
320 GNUNET_NETWORK_socket_listen (const struct GNUNET_NETWORK_Handle *desc,\r
321                               int backlog)\r
322 {\r
323   int ret;\r
324 \r
325   ret = listen (desc->fd, backlog);\r
326 #ifdef MINGW\r
327   if (SOCKET_ERROR == ret)\r
328     SetErrnoFromWinsockError (WSAGetLastError ());\r
329 #endif\r
330 \r
331   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
332 }\r
333 \r
334 /**\r
335  * Read data from a connected socket (always non-blocking).\r
336  * @param desc socket\r
337  * @param buffer buffer\r
338  * @param length length of buffer\r
339  */\r
340 ssize_t\r
341 GNUNET_NETWORK_socket_recv (const struct GNUNET_NETWORK_Handle * desc,\r
342                             void *buffer, size_t length)\r
343 {\r
344   int ret;\r
345   int flags;\r
346 \r
347   flags = 0;\r
348 #ifdef MSG_DONTWAIT\r
349   flags |= MSG_DONTWAIT;\r
350 #endif\r
351   ret = recv (desc->fd, buffer, length, flags);\r
352 #ifdef MINGW\r
353   if (SOCKET_ERROR == ret)\r
354     SetErrnoFromWinsockError (WSAGetLastError ());\r
355 #endif\r
356 \r
357   return ret;\r
358 }\r
359 \r
360 /**\r
361  * Send data (always non-blocking).\r
362  *\r
363  * @param desc socket\r
364  * @param buffer data to send\r
365  * @param length size of the buffer\r
366  * @return number of bytes sent, GNUNET_SYSERR on error\r
367  */\r
368 ssize_t\r
369 GNUNET_NETWORK_socket_send (const struct GNUNET_NETWORK_Handle * desc,\r
370                             const void *buffer, size_t length)\r
371 {\r
372   int ret;\r
373   int flags;\r
374 \r
375   flags = 0;\r
376 #ifdef MSG_DONTWAIT\r
377   flags |= MSG_DONTWAIT;\r
378 #endif\r
379 #ifdef MSG_NOSIGNAL\r
380   flags |= MSG_NOSIGNAL;\r
381 #endif\r
382   ret = send (desc->fd, buffer, length, flags);\r
383 #ifdef MINGW\r
384   if (SOCKET_ERROR == ret)\r
385     SetErrnoFromWinsockError (WSAGetLastError ());\r
386 #endif\r
387 \r
388   return ret;\r
389 }\r
390 \r
391 \r
392 /**\r
393  * Send data to a particular destination (always non-blocking).\r
394  * This function only works for UDP sockets.\r
395  *\r
396  * @param desc socket\r
397  * @param message data to send\r
398  * @param length size of the data\r
399  * @param dest_addr destination address\r
400  * @param dest_len length of address\r
401  * @return number of bytes sent, GNUNET_SYSERR on error\r
402  */\r
403 ssize_t\r
404 GNUNET_NETWORK_socket_sendto (const struct GNUNET_NETWORK_Handle * desc,\r
405                               const void *message, size_t length,\r
406                               const struct sockaddr * dest_addr,\r
407                               socklen_t dest_len)\r
408 {\r
409   int ret;\r
410   int flags;\r
411 \r
412   flags = 0;\r
413 #ifdef MSG_DONTWAIT\r
414   flags |= MSG_DONTWAIT;\r
415 #endif\r
416 #ifdef MSG_NOSIGNAL\r
417   flags |= MSG_NOSIGNAL;\r
418 #endif\r
419   ret = sendto (desc->fd, message, length, flags, dest_addr, dest_len);\r
420 #ifdef MINGW\r
421   if (SOCKET_ERROR == ret)\r
422     SetErrnoFromWinsockError (WSAGetLastError ());\r
423 #endif\r
424 \r
425   return ret;\r
426 }\r
427 \r
428 /**\r
429  * Set socket option\r
430  * @param fd socket\r
431  * @param level protocol level of the option\r
432  * @param option_name option identifier\r
433  * @param option_value value to set\r
434  * @param option_len size of option_value\r
435  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
436  */\r
437 int\r
438 GNUNET_NETWORK_socket_setsockopt (struct GNUNET_NETWORK_Handle *fd,\r
439                                   int level, int option_name,\r
440                                   const void *option_value,\r
441                                   socklen_t option_len)\r
442 {\r
443   int ret;\r
444 \r
445   ret = setsockopt (fd->fd, level, option_name, option_value, option_len);\r
446 #ifdef MINGW\r
447   if (SOCKET_ERROR == ret)\r
448     SetErrnoFromWinsockError (WSAGetLastError ());\r
449 #endif\r
450 \r
451   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
452 }\r
453 \r
454 \r
455 \r
456 /**\r
457  * Create a new socket.  Configure it for non-blocking IO and\r
458  * mark it as non-inheritable to child processes (set the\r
459  * close-on-exec flag).\r
460  *\r
461  * @param domain domain of the socket\r
462  * @param type socket type\r
463  * @param protocol network protocol\r
464  * @return new socket, NULL on error\r
465  */\r
466 struct GNUNET_NETWORK_Handle *\r
467 GNUNET_NETWORK_socket_create (int domain, int type, int protocol)\r
468 {\r
469   struct GNUNET_NETWORK_Handle *ret;\r
470 \r
471   ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle));\r
472   ret->fd = socket (domain, type, protocol);\r
473   if (INVALID_SOCKET == ret->fd)\r
474     {\r
475 #ifdef MINGW\r
476       SetErrnoFromWinsockError (WSAGetLastError ());\r
477 #endif\r
478       GNUNET_free (ret);\r
479       return NULL;\r
480     }\r
481 #ifndef MINGW\r
482   if (ret->fd >= FD_SETSIZE)\r
483     {\r
484       close (ret->fd);\r
485       GNUNET_free (ret);\r
486       errno = EMFILE;\r
487       return NULL;\r
488     }\r
489 #endif\r
490 \r
491   if (GNUNET_SYSERR == socket_set_blocking (ret, GNUNET_NO))\r
492     {\r
493       /* we might want to treat this one as fatal... */\r
494       GNUNET_break (0);\r
495       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ret));\r
496       return NULL; \r
497     }\r
498 #ifndef MINGW\r
499   if (GNUNET_OK != socket_set_inheritable (ret))\r
500     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,\r
501                          "socket_set_inheritable");\r
502 #endif\r
503 #ifdef DARWIN\r
504   socket_set_nosigpipe (ret);  \r
505 #endif\r
506   if (type == SOCK_STREAM)\r
507     socket_set_nodelay (ret);\r
508 \r
509   return ret;\r
510 }\r
511 \r
512 /**\r
513  * Shut down socket operations\r
514  * @param desc socket\r
515  * @param how type of shutdown\r
516  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise\r
517  */\r
518 int\r
519 GNUNET_NETWORK_socket_shutdown (struct GNUNET_NETWORK_Handle *desc,\r
520                                 int how)\r
521 {\r
522   int ret;\r
523 \r
524   ret = shutdown (desc->fd, how);\r
525 #ifdef MINGW\r
526   if (ret != 0)\r
527     SetErrnoFromWinsockError (WSAGetLastError ());\r
528 #endif\r
529 \r
530   return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;\r
531 }\r
532 \r
533 \r
534 /**\r
535  * Reset FD set\r
536  * @param fds fd set\r
537  */\r
538 void\r
539 GNUNET_NETWORK_fdset_zero (struct GNUNET_NETWORK_FDSet *fds)\r
540 {\r
541   FD_ZERO (&fds->sds);\r
542   fds->nsds = 0;\r
543 #ifdef MINGW\r
544   GNUNET_CONTAINER_slist_clear (fds->handles);\r
545 #endif\r
546 }\r
547 \r
548 \r
549 /**\r
550  * Add a socket to the FD set\r
551  * @param fds fd set\r
552  * @param desc socket to add\r
553  */\r
554 void\r
555 GNUNET_NETWORK_fdset_set (struct GNUNET_NETWORK_FDSet *fds,\r
556                           const struct GNUNET_NETWORK_Handle *desc)\r
557 {\r
558   FD_SET (desc->fd, &fds->sds);\r
559 \r
560   if (desc->fd + 1 > fds->nsds)\r
561     fds->nsds = desc->fd + 1;\r
562 }\r
563 \r
564 \r
565 /**\r
566  * Check whether a socket is part of the fd set\r
567  * @param fds fd set\r
568  * @param desc socket\r
569  * @return 0 if the FD is not set\r
570  */\r
571 int\r
572 GNUNET_NETWORK_fdset_isset (const struct GNUNET_NETWORK_FDSet *fds,\r
573                             const struct GNUNET_NETWORK_Handle *desc)\r
574 {\r
575   return FD_ISSET (desc->fd, &fds->sds);\r
576 }\r
577 \r
578 \r
579 /**\r
580  * Add one fd set to another\r
581  * @param dst the fd set to add to\r
582  * @param src the fd set to add from\r
583  */\r
584 void\r
585 GNUNET_NETWORK_fdset_add (struct GNUNET_NETWORK_FDSet *dst,\r
586                           const struct GNUNET_NETWORK_FDSet *src)\r
587 {\r
588   int nfds;\r
589 \r
590   for (nfds = src->nsds; nfds > 0; nfds--)\r
591     if (FD_ISSET (nfds, &src->sds))\r
592       {\r
593         FD_SET (nfds, &dst->sds);\r
594         if (nfds + 1 > dst->nsds)\r
595           dst->nsds = nfds + 1;\r
596       }\r
597 }\r
598 \r
599 \r
600 /**\r
601  * Copy one fd set to another\r
602  * @param to destination\r
603  * @param from source\r
604  */\r
605 void\r
606 GNUNET_NETWORK_fdset_copy (struct GNUNET_NETWORK_FDSet *to,\r
607                            const struct GNUNET_NETWORK_FDSet *from)\r
608 {\r
609   FD_COPY (&from->sds, &to->sds);\r
610   to->nsds = from->nsds;\r
611 #ifdef MINGW\r
612   struct GNUNET_CONTAINER_SList_Iterator *iter;\r
613 \r
614 \r
615   GNUNET_CONTAINER_slist_clear (to->handles);\r
616 \r
617   for (iter = GNUNET_CONTAINER_slist_begin (from->handles);\r
618       GNUNET_CONTAINER_slist_end (iter) != GNUNET_YES; GNUNET_CONTAINER_slist_next (iter))\r
619     {\r
620       void *handle;\r
621       size_t len;\r
622 \r
623       handle = GNUNET_CONTAINER_slist_get (iter, &len);\r
624       GNUNET_CONTAINER_slist_add (to->handles, GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, handle, len);\r
625     }\r
626 #endif\r
627 }\r
628 \r
629 \r
630 /**\r
631  * Copy a native fd set\r
632  * @param to destination\r
633  * @param from native source set\r
634  * @param nfds the biggest socket number in from + 1\r
635  */\r
636 void\r
637 GNUNET_NETWORK_fdset_copy_native (struct GNUNET_NETWORK_FDSet *to,\r
638                                   const fd_set * from, int nfds)\r
639 {\r
640   FD_COPY (from, &to->sds);\r
641   to->nsds = nfds;\r
642 }\r
643 \r
644 \r
645 /**\r
646  * Add a file handle to the fd set\r
647  * @param fds fd set\r
648  * @param h the file handle to add\r
649  */\r
650 void\r
651 GNUNET_NETWORK_fdset_handle_set (struct GNUNET_NETWORK_FDSet *fds,\r
652                                  const struct GNUNET_DISK_FileHandle *h)\r
653 {\r
654 #ifdef MINGW\r
655   HANDLE hw;\r
656 \r
657   GNUNET_DISK_internal_file_handle_ (h, &hw, sizeof (HANDLE));\r
658   GNUNET_CONTAINER_slist_add (fds->handles, GNUNET_NO, &hw, sizeof (HANDLE));\r
659 #else\r
660   int fd;\r
661 \r
662   GNUNET_DISK_internal_file_handle_ (h, &fd, sizeof (int));\r
663   FD_SET (fd, &fds->sds);\r
664   if (fd + 1 > fds->nsds)\r
665     fds->nsds = fd + 1;\r
666 #endif\r
667 }\r
668 \r
669 /**\r
670  * Check if a file handle is part of an fd set\r
671  * @param fds fd set\r
672  * @param h file handle\r
673  * @return GNUNET_YES if the file handle is part of the set\r
674  */\r
675 int\r
676 GNUNET_NETWORK_fdset_handle_isset (const struct GNUNET_NETWORK_FDSet *fds,\r
677                                    const struct GNUNET_DISK_FileHandle *h)\r
678 {\r
679 #ifdef MINGW\r
680   return GNUNET_CONTAINER_slist_contains (fds->handles, h->h, sizeof (HANDLE));\r
681 #else\r
682   return FD_ISSET (h->fd, &fds->sds);\r
683 #endif\r
684 }\r
685 \r
686 /**\r
687  * Checks if two fd sets overlap\r
688  * @param fds1 first fd set\r
689  * @param fds2 second fd set\r
690  * @return GNUNET_YES if they do overlap, GNUNET_NO otherwise\r
691  */\r
692 int\r
693 GNUNET_NETWORK_fdset_overlap (const struct GNUNET_NETWORK_FDSet *fds1,\r
694                               const struct GNUNET_NETWORK_FDSet *fds2)\r
695 {\r
696   int nfds;\r
697 \r
698   nfds = fds1->nsds;\r
699   if (nfds < fds2->nsds)\r
700     nfds = fds2->nsds;\r
701 \r
702   for (; nfds >= 0; nfds--)\r
703     if (FD_ISSET (nfds, &fds1->sds) && FD_ISSET (nfds, &fds2->sds))\r
704       return GNUNET_YES;\r
705 \r
706   return GNUNET_NO;\r
707 }\r
708 \r
709 /**\r
710  * Creates an fd set\r
711  * @return a new fd set\r
712  */\r
713 struct GNUNET_NETWORK_FDSet *\r
714 GNUNET_NETWORK_fdset_create ()\r
715 {\r
716   struct GNUNET_NETWORK_FDSet *fds;\r
717 \r
718   fds = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_FDSet));\r
719 #ifdef MINGW\r
720   fds->handles = GNUNET_CONTAINER_slist_create ();\r
721 #endif\r
722   GNUNET_NETWORK_fdset_zero (fds);\r
723   return fds;\r
724 }\r
725 \r
726 \r
727 /**\r
728  * Releases the associated memory of an fd set\r
729  * @param fds fd set\r
730  */\r
731 void\r
732 GNUNET_NETWORK_fdset_destroy (struct GNUNET_NETWORK_FDSet *fds)\r
733 {\r
734 #ifdef MINGW\r
735   GNUNET_CONTAINER_slist_destroy (fds->handles);\r
736 #endif\r
737   GNUNET_free (fds);\r
738 }\r
739 \r
740 \r
741 /**\r
742  * Check if sockets meet certain conditions\r
743  * @param rfds set of sockets to be checked for readability\r
744  * @param wfds set of sockets to be checked for writability\r
745  * @param efds set of sockets to be checked for exceptions\r
746  * @param timeout relative value when to return\r
747  * @return number of selected sockets, GNUNET_SYSERR on error\r
748  */\r
749 int\r
750 GNUNET_NETWORK_socket_select (struct GNUNET_NETWORK_FDSet *rfds,\r
751                               struct GNUNET_NETWORK_FDSet *wfds,\r
752                               struct GNUNET_NETWORK_FDSet *efds,\r
753                               const struct GNUNET_TIME_Relative timeout)\r
754 {\r
755   int nfds;\r
756 \r
757   nfds = 0;\r
758   if (NULL != rfds)\r
759     nfds = rfds->nsds;\r
760   if (NULL != wfds)\r
761     nfds = GNUNET_MAX (nfds, wfds->nsds);\r
762   if (NULL != efds)\r
763     nfds = GNUNET_MAX (nfds, efds->nsds);\r
764 \r
765 #ifndef MINGW\r
766   struct timeval tv;\r
767 \r
768   tv.tv_sec = timeout.value / GNUNET_TIME_UNIT_SECONDS.value;\r
769   tv.tv_usec = 1000 * (timeout.value - (tv.tv_sec * GNUNET_TIME_UNIT_SECONDS.value));\r
770   if ( (nfds == 0) &&\r
771        (timeout.value == GNUNET_TIME_UNIT_FOREVER_REL.value) )\r
772     {\r
773       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,\r
774                   _("Fatal internal logic error, process hangs in `%s' (abort with CTRL-C)!\n"),\r
775                   "select");\r
776       GNUNET_break (0);\r
777     }\r
778   return select (nfds + 1, \r
779                  (rfds != NULL) ? &rfds->sds : NULL, \r
780                  (wfds != NULL) ? &wfds->sds : NULL,\r
781                  (efds != NULL) ? &efds->sds : NULL, \r
782                  (timeout.value == GNUNET_TIME_UNIT_FOREVER_REL.value) \r
783                  ? NULL \r
784                  : &tv);\r
785 #else\r
786   DWORD limit;\r
787   fd_set sock_read, sock_write, sock_except;\r
788   fd_set aread, awrite, aexcept;\r
789   int i;\r
790   struct timeval tvslice;\r
791   int retcode;\r
792   DWORD ms_total;\r
793 \r
794 #define SAFE_FD_ISSET(fd, set)  (set != NULL && FD_ISSET(fd, set))\r
795 \r
796   /* calculate how long we need to wait in milliseconds */\r
797   if (timeout.value == GNUNET_TIME_UNIT_FOREVER_REL.value)\r
798     ms_total = INFINITE;\r
799   else\r
800     ms_total = timeout.value / GNUNET_TIME_UNIT_MILLISECONDS.value;\r
801 \r
802   /* select() may be used as a portable way to sleep */\r
803   if (!(rfds || wfds || efds))\r
804     {\r
805       Sleep (ms_total);\r
806       return 0;\r
807     }\r
808 \r
809   if (rfds)\r
810     sock_read = rfds->sds;\r
811   else\r
812     FD_ZERO(&sock_read);\r
813 \r
814   if (wfds)\r
815     sock_write = wfds->sds;\r
816   else\r
817     FD_ZERO(&sock_write);\r
818 \r
819   if (efds)\r
820     sock_except = efds->sds;\r
821   else\r
822     FD_ZERO(&sock_except);\r
823 \r
824   /* multiplex between winsock select() and waiting on the handles */\r
825 \r
826   FD_ZERO (&aread);\r
827   FD_ZERO (&awrite);\r
828   FD_ZERO (&aexcept);\r
829 \r
830   limit = GetTickCount () + ms_total;\r
831   do\r
832     {\r
833       retcode = 0;\r
834 \r
835       if (nfds > 0)\r
836         {\r
837           /* overwrite the zero'd sets here; the select call\r
838            * will clear those that are not active */\r
839 \r
840           FD_COPY (&sock_read, &aread);\r
841           FD_COPY (&sock_write, &awrite);\r
842           FD_COPY (&sock_except, &aexcept);\r
843 \r
844           tvslice.tv_sec = 0;\r
845           tvslice.tv_usec = 100000;\r
846 \r
847           if ((retcode =\r
848                select (nfds + 1, &aread, &awrite, &aexcept,\r
849                        &tvslice)) == SOCKET_ERROR)\r
850             {\r
851               SetErrnoFromWinsockError (WSAGetLastError ());\r
852               if (errno == ENOTSOCK)\r
853                 errno = EBADF;\r
854 \r
855 #if DEBUG_SOCK\r
856             GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");\r
857 #endif\r
858 \r
859               goto select_loop_end;\r
860             }\r
861         }\r
862 \r
863       /* Poll read pipes */\r
864       if (rfds)\r
865         {\r
866           struct GNUNET_CONTAINER_SList_Iterator *i;\r
867           int on_next;\r
868 \r
869           on_next = GNUNET_NO;\r
870           for (i = GNUNET_CONTAINER_slist_begin (rfds->handles);\r
871                     GNUNET_CONTAINER_slist_end (i) != GNUNET_YES;\r
872                     on_next || GNUNET_CONTAINER_slist_next (i))\r
873             {\r
874               HANDLE h;\r
875               DWORD dwBytes;\r
876 \r
877               h = *(HANDLE *) GNUNET_CONTAINER_slist_get (i, NULL);\r
878               on_next = GNUNET_NO;\r
879 \r
880               if (!PeekNamedPipe (h, NULL, 0, NULL, &dwBytes, NULL))\r
881                 {\r
882                   GNUNET_CONTAINER_slist_erase (i);\r
883                   on_next = GNUNET_YES;\r
884 \r
885                   retcode = -1;\r
886                   SetErrnoFromWinError (GetLastError ());\r
887 #if DEBUG_SOCK\r
888                   GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "PeekNamedPipe");\r
889 #endif\r
890                   goto select_loop_end;\r
891                 }\r
892               else if (dwBytes)\r
893                 {\r
894                   retcode++;\r
895                 }\r
896               else\r
897                 {\r
898                   GNUNET_CONTAINER_slist_erase (i);\r
899                   on_next = GNUNET_YES;\r
900                 }\r
901             }\r
902         }\r
903 \r
904       /* Poll for faulty pipes */\r
905       if (efds)\r
906         {\r
907           struct GNUNET_CONTAINER_SList_Iterator *i;\r
908           int on_next;\r
909 \r
910           on_next = GNUNET_NO;\r
911           for (i = GNUNET_CONTAINER_slist_begin (efds->handles);\r
912                     GNUNET_CONTAINER_slist_end (i) != GNUNET_YES;\r
913                     on_next || GNUNET_CONTAINER_slist_next (i))\r
914             {\r
915               HANDLE h;\r
916               DWORD dwBytes;\r
917 \r
918               h = *(HANDLE *) GNUNET_CONTAINER_slist_get (i, NULL);\r
919 \r
920               if (PeekNamedPipe (h, NULL, 0, NULL, &dwBytes, NULL))\r
921                 {\r
922                   GNUNET_CONTAINER_slist_erase (i);\r
923                   on_next = GNUNET_YES;\r
924 \r
925                   retcode++;\r
926                 }\r
927               else\r
928                 on_next = GNUNET_NO;\r
929             }\r
930         }\r
931 \r
932       /* FIXME */\r
933       if (wfds)\r
934         GNUNET_assert (GNUNET_CONTAINER_slist_count (wfds->handles) == 0);\r
935 \r
936       /* Check for closed sockets */\r
937       for (i = 0; i < nfds; i++)\r
938         {\r
939           if (SAFE_FD_ISSET (i, &sock_read))\r
940             {\r
941               struct sockaddr addr;\r
942               int len;\r
943 \r
944               if (getpeername (i, &addr, &len) == SOCKET_ERROR)\r
945                 {\r
946                   int err, len;\r
947 \r
948                   len = sizeof (err);\r
949                   if (getsockopt\r
950                       (i, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == 0\r
951                       && err == WSAENOTCONN)\r
952                     {\r
953                       if (!SAFE_FD_ISSET (i, &aread))\r
954                         {\r
955                           FD_SET (i, &aread);\r
956                           retcode++;\r
957                         }\r
958                     }\r
959                 }\r
960             }\r
961         }\r
962 \r
963     select_loop_end:;\r
964     }\r
965   while (retcode == 0 && (ms_total == INFINITE || GetTickCount () < limit));\r
966 \r
967   if (retcode != -1)\r
968     {\r
969       if (rfds)\r
970         {\r
971           GNUNET_NETWORK_fdset_zero (rfds);\r
972           GNUNET_NETWORK_fdset_copy_native (rfds, &aread, retcode);\r
973         }\r
974 \r
975       if (wfds)\r
976         {\r
977           GNUNET_NETWORK_fdset_zero (wfds);\r
978           GNUNET_NETWORK_fdset_copy_native (wfds, &awrite, retcode);\r
979         }\r
980 \r
981       if (efds)\r
982         {\r
983           GNUNET_NETWORK_fdset_zero (efds);\r
984           GNUNET_NETWORK_fdset_copy_native (efds, &aexcept, retcode);\r
985         }\r
986     }\r
987 \r
988   return retcode;\r
989 #endif\r
990 }\r
991 \r
992 \r
993 /* end of network.c */\r