diff options
Diffstat (limited to 'src/util/network.c')
-rw-r--r-- | src/util/network.c | 1239 |
1 files changed, 1239 insertions, 0 deletions
diff --git a/src/util/network.c b/src/util/network.c new file mode 100644 index 000000000..22c1ca632 --- /dev/null +++ b/src/util/network.c | |||
@@ -0,0 +1,1239 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2009 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/network/network.c | ||
23 | * @brief basic, low-level TCP networking interface | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * This code is rather complex. Only modify it if you | ||
27 | * 1) Have a NEW testcase showing that the new code | ||
28 | * is needed and correct | ||
29 | * 2) All EXISTING testcases pass with the new code | ||
30 | * These rules should apply in general, but for this | ||
31 | * module they are VERY, VERY important. | ||
32 | * | ||
33 | * TODO: | ||
34 | * - can we merge receive_ready and receive_again? | ||
35 | * - can we integrate the nth.timeout_task with the write_task's timeout? | ||
36 | */ | ||
37 | |||
38 | #include "platform.h" | ||
39 | #include "gnunet_common.h" | ||
40 | #include "gnunet_network_lib.h" | ||
41 | #include "gnunet_scheduler_lib.h" | ||
42 | |||
43 | #define DEBUG_NETWORK GNUNET_NO | ||
44 | |||
45 | struct GNUNET_NETWORK_TransmitHandle | ||
46 | { | ||
47 | |||
48 | /** | ||
49 | * Function to call if the send buffer has notify_size | ||
50 | * bytes available. | ||
51 | */ | ||
52 | GNUNET_NETWORK_TransmitReadyNotify notify_ready; | ||
53 | |||
54 | /** | ||
55 | * Closure for notify_ready. | ||
56 | */ | ||
57 | void *notify_ready_cls; | ||
58 | |||
59 | /** | ||
60 | * Our socket handle. | ||
61 | */ | ||
62 | struct GNUNET_NETWORK_SocketHandle *sh; | ||
63 | |||
64 | /** | ||
65 | * Timeout for receiving (in absolute time). | ||
66 | */ | ||
67 | struct GNUNET_TIME_Absolute transmit_timeout; | ||
68 | |||
69 | /** | ||
70 | * Task called on timeout. | ||
71 | */ | ||
72 | GNUNET_SCHEDULER_TaskIdentifier timeout_task; | ||
73 | |||
74 | /** | ||
75 | * At what number of bytes available in the | ||
76 | * write buffer should the notify method be called? | ||
77 | */ | ||
78 | size_t notify_size; | ||
79 | |||
80 | }; | ||
81 | |||
82 | /** | ||
83 | * @brief handle for a network socket | ||
84 | */ | ||
85 | struct GNUNET_NETWORK_SocketHandle | ||
86 | { | ||
87 | |||
88 | /** | ||
89 | * Scheduler that was used for the connect task. | ||
90 | */ | ||
91 | struct GNUNET_SCHEDULER_Handle *sched; | ||
92 | |||
93 | /** | ||
94 | * Address information for connect (may be NULL). | ||
95 | */ | ||
96 | struct addrinfo *ai; | ||
97 | |||
98 | /** | ||
99 | * Index for the next struct addrinfo for connect attempts (may be NULL) | ||
100 | */ | ||
101 | struct addrinfo *ai_pos; | ||
102 | |||
103 | /** | ||
104 | * Network address of the other end-point, may be NULL. | ||
105 | */ | ||
106 | struct sockaddr *addr; | ||
107 | |||
108 | /** | ||
109 | * Pointer to our write buffer. | ||
110 | */ | ||
111 | char *write_buffer; | ||
112 | |||
113 | /** | ||
114 | * Size of our write buffer. | ||
115 | */ | ||
116 | size_t write_buffer_size; | ||
117 | |||
118 | /** | ||
119 | * Current write-offset in write buffer (where | ||
120 | * would we write next). | ||
121 | */ | ||
122 | size_t write_buffer_off; | ||
123 | |||
124 | /** | ||
125 | * Current read-offset in write buffer (how many | ||
126 | * bytes have already been send). | ||
127 | */ | ||
128 | size_t write_buffer_pos; | ||
129 | |||
130 | /** | ||
131 | * Length of addr. | ||
132 | */ | ||
133 | socklen_t addrlen; | ||
134 | |||
135 | /** | ||
136 | * Connect task that we may need to wait for. | ||
137 | */ | ||
138 | GNUNET_SCHEDULER_TaskIdentifier connect_task; | ||
139 | |||
140 | /** | ||
141 | * Read task that we may need to wait for. | ||
142 | */ | ||
143 | GNUNET_SCHEDULER_TaskIdentifier read_task; | ||
144 | |||
145 | /** | ||
146 | * Write task that we may need to wait for. | ||
147 | */ | ||
148 | GNUNET_SCHEDULER_TaskIdentifier write_task; | ||
149 | |||
150 | /** | ||
151 | * The handle we return for GNUNET_NETWORK_notify_transmit_ready. | ||
152 | */ | ||
153 | struct GNUNET_NETWORK_TransmitHandle nth; | ||
154 | |||
155 | /** | ||
156 | * Underlying OS's socket, set to -1 after fatal errors. | ||
157 | */ | ||
158 | int sock; | ||
159 | |||
160 | /** | ||
161 | * Port to connect to. | ||
162 | */ | ||
163 | uint16_t port; | ||
164 | |||
165 | /** | ||
166 | * Function to call on data received, NULL | ||
167 | * if no receive is pending. | ||
168 | */ | ||
169 | GNUNET_NETWORK_Receiver receiver; | ||
170 | |||
171 | /** | ||
172 | * Closure for receiver. | ||
173 | */ | ||
174 | void *receiver_cls; | ||
175 | |||
176 | /** | ||
177 | * Timeout for receiving (in absolute time). | ||
178 | */ | ||
179 | struct GNUNET_TIME_Absolute receive_timeout; | ||
180 | |||
181 | /** | ||
182 | * Maximum number of bytes to read | ||
183 | * (for receiving). | ||
184 | */ | ||
185 | size_t max; | ||
186 | |||
187 | }; | ||
188 | |||
189 | |||
190 | /** | ||
191 | * Create a socket handle by boxing an existing OS socket. The OS | ||
192 | * socket should henceforth be no longer used directly. | ||
193 | * GNUNET_socket_destroy will close it. | ||
194 | * | ||
195 | * @param sched scheduler to use | ||
196 | * @param osSocket existing socket to box | ||
197 | * @param maxbuf maximum write buffer size for the socket (use | ||
198 | * 0 for sockets that need no write buffers, such as listen sockets) | ||
199 | * @return the boxed socket handle | ||
200 | */ | ||
201 | struct GNUNET_NETWORK_SocketHandle * | ||
202 | GNUNET_NETWORK_socket_create_from_existing (struct GNUNET_SCHEDULER_Handle | ||
203 | *sched, int osSocket, | ||
204 | size_t maxbuf) | ||
205 | { | ||
206 | struct GNUNET_NETWORK_SocketHandle *ret; | ||
207 | ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf); | ||
208 | ret->write_buffer = (char *) &ret[1]; | ||
209 | ret->write_buffer_size = maxbuf; | ||
210 | ret->sock = osSocket; | ||
211 | ret->sched = sched; | ||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | |||
216 | /** | ||
217 | * Create a socket handle by accepting on a listen socket. This | ||
218 | * function may block if the listen socket has no connection ready. | ||
219 | * | ||
220 | * @param sched scheduler to use | ||
221 | * @param access function to use to check if access is allowed | ||
222 | * @param access_cls closure for access | ||
223 | * @param lsock listen socket | ||
224 | * @param maxbuf maximum write buffer size for the socket (use | ||
225 | * 0 for sockets that need no write buffers, such as listen sockets) | ||
226 | * @return the socket handle, NULL on error | ||
227 | */ | ||
228 | struct GNUNET_NETWORK_SocketHandle * | ||
229 | GNUNET_NETWORK_socket_create_from_accept (struct GNUNET_SCHEDULER_Handle | ||
230 | *sched, | ||
231 | GNUNET_NETWORK_AccessCheck access, | ||
232 | void *access_cls, int lsock, | ||
233 | size_t maxbuf) | ||
234 | { | ||
235 | struct GNUNET_NETWORK_SocketHandle *ret; | ||
236 | char addr[32]; | ||
237 | char msg[INET6_ADDRSTRLEN]; | ||
238 | socklen_t addrlen; | ||
239 | int fam; | ||
240 | int fd; | ||
241 | int aret; | ||
242 | struct sockaddr_in *v4; | ||
243 | struct sockaddr_in6 *v6; | ||
244 | struct sockaddr *sa; | ||
245 | void *uaddr; | ||
246 | |||
247 | addrlen = sizeof (addr); | ||
248 | fd = accept (lsock, (struct sockaddr *) &addr, &addrlen); | ||
249 | if (fd == -1) | ||
250 | { | ||
251 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "accept"); | ||
252 | return NULL; | ||
253 | } | ||
254 | if (0 != fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC)) | ||
255 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
256 | "fcntl"); | ||
257 | if (addrlen > sizeof (addr)) | ||
258 | { | ||
259 | GNUNET_break (0); | ||
260 | GNUNET_break (0 == CLOSE (fd)); | ||
261 | return NULL; | ||
262 | } | ||
263 | |||
264 | sa = (struct sockaddr *) addr; | ||
265 | v6 = (struct sockaddr_in6 *) addr; | ||
266 | if ((sa->sa_family == AF_INET6) && (IN6_IS_ADDR_V4MAPPED (&v6->sin6_addr))) | ||
267 | { | ||
268 | /* convert to V4 address */ | ||
269 | v4 = GNUNET_malloc (sizeof (struct sockaddr_in)); | ||
270 | memset (v4, 0, sizeof (struct sockaddr_in)); | ||
271 | v4->sin_family = AF_INET; | ||
272 | memcpy (&v4->sin_addr, | ||
273 | &((char *) &v6->sin6_addr)[sizeof (struct in6_addr) - | ||
274 | sizeof (struct in_addr)], | ||
275 | sizeof (struct in_addr)); | ||
276 | v4->sin_port = v6->sin6_port; | ||
277 | uaddr = v4; | ||
278 | addrlen = sizeof (struct sockaddr_in); | ||
279 | } | ||
280 | else | ||
281 | { | ||
282 | uaddr = GNUNET_malloc (addrlen); | ||
283 | memcpy (uaddr, addr, addrlen); | ||
284 | } | ||
285 | |||
286 | if ((access != NULL) && | ||
287 | (GNUNET_YES != (aret = access (access_cls, uaddr, addrlen)))) | ||
288 | { | ||
289 | if (aret == GNUNET_NO) | ||
290 | { | ||
291 | fam = ((struct sockaddr *) addr)->sa_family; | ||
292 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
293 | _("Access denied to `%s'\n"), | ||
294 | inet_ntop (fam, | ||
295 | (fam == AF_INET6) | ||
296 | ? (const void *) &((struct sockaddr_in6 *) | ||
297 | &addr)-> | ||
298 | sin6_addr : (const void *) | ||
299 | &((struct sockaddr_in *) &addr)->sin_addr, | ||
300 | msg, sizeof (msg))); | ||
301 | } | ||
302 | GNUNET_break (0 == SHUTDOWN (fd, SHUT_RDWR)); | ||
303 | GNUNET_break (0 == CLOSE (fd)); | ||
304 | GNUNET_free (uaddr); | ||
305 | return NULL; | ||
306 | } | ||
307 | ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf); | ||
308 | ret->write_buffer = (char *) &ret[1]; | ||
309 | ret->write_buffer_size = maxbuf; | ||
310 | ret->addr = uaddr; | ||
311 | ret->addrlen = addrlen; | ||
312 | ret->sock = fd; | ||
313 | ret->sched = sched; | ||
314 | return ret; | ||
315 | } | ||
316 | |||
317 | /** | ||
318 | * Obtain the network address of the other party. | ||
319 | * | ||
320 | * @param sock the client to get the address for | ||
321 | * @param addr where to store the address | ||
322 | * @param addrlen where to store the length of the address | ||
323 | * @return GNUNET_OK on success | ||
324 | */ | ||
325 | int | ||
326 | GNUNET_NETWORK_socket_get_address (struct GNUNET_NETWORK_SocketHandle *sock, | ||
327 | void **addr, size_t * addrlen) | ||
328 | { | ||
329 | if ((sock->addr == NULL) || (sock->addrlen == 0)) | ||
330 | return GNUNET_NO; | ||
331 | *addr = GNUNET_malloc (sock->addrlen); | ||
332 | memcpy (*addr, sock->addr, sock->addrlen); | ||
333 | *addrlen = sock->addrlen; | ||
334 | return GNUNET_OK; | ||
335 | } | ||
336 | |||
337 | |||
338 | /** | ||
339 | * Set if a socket should use blocking or non-blocking IO. | ||
340 | * | ||
341 | * @return GNUNET_OK on success, GNUNET_SYSERR on error | ||
342 | */ | ||
343 | static int | ||
344 | socket_set_blocking (int handle, int doBlock) | ||
345 | { | ||
346 | #if MINGW | ||
347 | u_long mode; | ||
348 | mode = !doBlock; | ||
349 | #if HAVE_PLIBC_FD | ||
350 | if (ioctlsocket (plibc_fd_get_handle (handle), FIONBIO, &mode) == | ||
351 | SOCKET_ERROR) | ||
352 | { | ||
353 | SetErrnoFromWinsockError (WSAGetLastError ()); | ||
354 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket"); | ||
355 | return GNUNET_SYSERR; | ||
356 | } | ||
357 | #else | ||
358 | if (ioctlsocket (handle, FIONBIO, &mode) == SOCKET_ERROR) | ||
359 | { | ||
360 | SetErrnoFromWinsockError (WSAGetLastError ()); | ||
361 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket"); | ||
362 | return GNUNET_SYSERR; | ||
363 | } | ||
364 | #endif | ||
365 | /* store the blocking mode */ | ||
366 | #if HAVE_PLIBC_FD | ||
367 | plibc_fd_set_blocking (handle, doBlock); | ||
368 | #else | ||
369 | __win_SetHandleBlockingMode (handle, doBlock); | ||
370 | #endif | ||
371 | return GNUNET_OK; | ||
372 | |||
373 | #else | ||
374 | /* not MINGW */ | ||
375 | int flags = fcntl (handle, F_GETFL); | ||
376 | if (flags == -1) | ||
377 | { | ||
378 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl"); | ||
379 | return GNUNET_SYSERR; | ||
380 | } | ||
381 | if (doBlock) | ||
382 | flags &= ~O_NONBLOCK; | ||
383 | else | ||
384 | flags |= O_NONBLOCK; | ||
385 | if (0 != fcntl (handle, F_SETFL, flags)) | ||
386 | { | ||
387 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl"); | ||
388 | return GNUNET_SYSERR; | ||
389 | } | ||
390 | return GNUNET_OK; | ||
391 | #endif | ||
392 | } | ||
393 | |||
394 | |||
395 | /** | ||
396 | * Initiate asynchronous TCP connect request. | ||
397 | * | ||
398 | * @param sock what socket to connect | ||
399 | * @return GNUNET_SYSERR error (no more addresses to try) | ||
400 | */ | ||
401 | static int | ||
402 | try_connect (struct GNUNET_NETWORK_SocketHandle *sock) | ||
403 | { | ||
404 | int s; | ||
405 | |||
406 | if (sock->addr != NULL) | ||
407 | { | ||
408 | GNUNET_free (sock->addr); | ||
409 | sock->addr = NULL; | ||
410 | sock->addrlen = 0; | ||
411 | } | ||
412 | while (1) | ||
413 | { | ||
414 | if (sock->ai_pos == NULL) | ||
415 | { | ||
416 | /* no more addresses to try, fatal! */ | ||
417 | return GNUNET_SYSERR; | ||
418 | } | ||
419 | switch (sock->ai_pos->ai_family) | ||
420 | { | ||
421 | case AF_INET: | ||
422 | ((struct sockaddr_in *) sock->ai_pos->ai_addr)->sin_port = | ||
423 | htons (sock->port); | ||
424 | break; | ||
425 | case AF_INET6: | ||
426 | ((struct sockaddr_in6 *) sock->ai_pos->ai_addr)->sin6_port = | ||
427 | htons (sock->port); | ||
428 | break; | ||
429 | default: | ||
430 | sock->ai_pos = sock->ai_pos->ai_next; | ||
431 | continue; | ||
432 | } | ||
433 | s = SOCKET (sock->ai_pos->ai_family, SOCK_STREAM, 0); | ||
434 | if (s == -1) | ||
435 | { | ||
436 | /* maybe unsupported address family, try next */ | ||
437 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "socket"); | ||
438 | sock->ai_pos = sock->ai_pos->ai_next; | ||
439 | continue; | ||
440 | } | ||
441 | if (0 != fcntl (s, F_SETFD, fcntl (s, F_GETFD) | FD_CLOEXEC)) | ||
442 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
443 | "fcntl"); | ||
444 | if (GNUNET_SYSERR == socket_set_blocking (s, GNUNET_NO)) | ||
445 | { | ||
446 | /* we'll treat this one as fatal */ | ||
447 | GNUNET_break (0 == CLOSE (s)); | ||
448 | return GNUNET_SYSERR; | ||
449 | } | ||
450 | if ((0 != CONNECT (s, | ||
451 | sock->ai_pos->ai_addr, | ||
452 | sock->ai_pos->ai_addrlen)) && (errno != EINPROGRESS)) | ||
453 | { | ||
454 | /* maybe refused / unsupported address, try next */ | ||
455 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "connect"); | ||
456 | GNUNET_break (0 == CLOSE (s)); | ||
457 | continue; | ||
458 | } | ||
459 | break; | ||
460 | } | ||
461 | /* got one! copy address information! */ | ||
462 | sock->addrlen = sock->ai_pos->ai_addrlen; | ||
463 | sock->addr = GNUNET_malloc (sock->addrlen); | ||
464 | memcpy (sock->addr, sock->ai_pos->ai_addr, sock->addrlen); | ||
465 | sock->ai_pos = sock->ai_pos->ai_next; | ||
466 | sock->sock = s; | ||
467 | return GNUNET_OK; | ||
468 | } | ||
469 | |||
470 | |||
471 | /** | ||
472 | * Scheduler let us know that we're either ready to | ||
473 | * write on the socket OR connect timed out. Do the | ||
474 | * right thing. | ||
475 | */ | ||
476 | static void | ||
477 | connect_continuation (void *cls, | ||
478 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
479 | { | ||
480 | struct GNUNET_NETWORK_SocketHandle *sock = cls; | ||
481 | unsigned int len; | ||
482 | int error; | ||
483 | |||
484 | /* nobody needs to wait for us anymore... */ | ||
485 | sock->connect_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
486 | /* Note: write-ready does NOT mean connect succeeded, | ||
487 | we need to use getsockopt to be sure */ | ||
488 | len = sizeof (error); | ||
489 | errno = 0; | ||
490 | error = 0; | ||
491 | if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) || | ||
492 | (0 != getsockopt (sock->sock, SOL_SOCKET, SO_ERROR, &error, &len)) || | ||
493 | (error != 0) || (errno != 0)) | ||
494 | { | ||
495 | /* connect failed / timed out */ | ||
496 | GNUNET_break (0 == CLOSE (sock->sock)); | ||
497 | sock->sock = -1; | ||
498 | if (GNUNET_SYSERR == try_connect (sock)) | ||
499 | { | ||
500 | /* failed for good */ | ||
501 | GNUNET_break (sock->ai_pos == NULL); | ||
502 | freeaddrinfo (sock->ai); | ||
503 | sock->ai = NULL; | ||
504 | return; | ||
505 | } | ||
506 | sock->connect_task = GNUNET_SCHEDULER_add_write (tc->sched, GNUNET_NO, /* abort on shutdown */ | ||
507 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
508 | GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, | ||
509 | GNUNET_NETWORK_CONNECT_RETRY_TIMEOUT, | ||
510 | sock->sock, | ||
511 | &connect_continuation, | ||
512 | sock); | ||
513 | return; | ||
514 | } | ||
515 | /* connect succeeded! clean up "ai" */ | ||
516 | #if DEBUG_NETWORK | ||
517 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection succeeded!\n"); | ||
518 | #endif | ||
519 | freeaddrinfo (sock->ai); | ||
520 | sock->ai_pos = NULL; | ||
521 | sock->ai = NULL; | ||
522 | } | ||
523 | |||
524 | |||
525 | /** | ||
526 | * Create a socket handle by (asynchronously) connecting to a host. | ||
527 | * This function returns immediately, even if the connection has not | ||
528 | * yet been established. This function only creates TCP connections. | ||
529 | * | ||
530 | * @param sched scheduler to use | ||
531 | * @param hostname name of the host to connect to | ||
532 | * @param port port to connect to | ||
533 | * @param maxbuf maximum write buffer size for the socket (use | ||
534 | * 0 for sockets that need no write buffers, such as listen sockets) | ||
535 | * @return the socket handle | ||
536 | */ | ||
537 | struct GNUNET_NETWORK_SocketHandle * | ||
538 | GNUNET_NETWORK_socket_create_from_connect (struct GNUNET_SCHEDULER_Handle | ||
539 | *sched, const char *hostname, | ||
540 | uint16_t port, size_t maxbuf) | ||
541 | { | ||
542 | struct GNUNET_NETWORK_SocketHandle *ret; | ||
543 | struct addrinfo hints; | ||
544 | int ec; | ||
545 | |||
546 | ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf); | ||
547 | ret->sock = -1; | ||
548 | ret->sched = sched; | ||
549 | ret->write_buffer = (char *) &ret[1]; | ||
550 | ret->write_buffer_size = maxbuf; | ||
551 | ret->port = port; | ||
552 | memset (&hints, 0, sizeof (hints)); | ||
553 | hints.ai_family = AF_UNSPEC; | ||
554 | hints.ai_socktype = SOCK_STREAM; | ||
555 | if (0 != (ec = getaddrinfo (hostname, NULL, &hints, &ret->ai))) | ||
556 | { | ||
557 | GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK, | ||
558 | "`%s' failed for hostname `%s': %s\n", | ||
559 | "getaddrinfo", hostname, gai_strerror (ec)); | ||
560 | GNUNET_free (ret); | ||
561 | return NULL; | ||
562 | } | ||
563 | ret->ai_pos = ret->ai; | ||
564 | if (GNUNET_SYSERR == try_connect (ret)) | ||
565 | { | ||
566 | freeaddrinfo (ret->ai); | ||
567 | GNUNET_free (ret); | ||
568 | return NULL; | ||
569 | } | ||
570 | ret->connect_task = GNUNET_SCHEDULER_add_write (sched, GNUNET_NO, /* abort on shutdown */ | ||
571 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
572 | GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, | ||
573 | GNUNET_NETWORK_CONNECT_RETRY_TIMEOUT, | ||
574 | ret->sock, | ||
575 | &connect_continuation, ret); | ||
576 | return ret; | ||
577 | |||
578 | } | ||
579 | |||
580 | |||
581 | /** | ||
582 | * Create a socket handle by (asynchronously) connecting to a host. | ||
583 | * This function returns immediately, even if the connection has not | ||
584 | * yet been established. This function only creates TCP connections. | ||
585 | * | ||
586 | * @param sched scheduler to use | ||
587 | * @param af_family address family to use | ||
588 | * @param serv_addr server address | ||
589 | * @param addrlen length of server address | ||
590 | * @param maxbuf maximum write buffer size for the socket (use | ||
591 | * 0 for sockets that need no write buffers, such as listen sockets) | ||
592 | * @return the socket handle | ||
593 | */ | ||
594 | struct GNUNET_NETWORK_SocketHandle * | ||
595 | GNUNET_NETWORK_socket_create_from_sockaddr (struct GNUNET_SCHEDULER_Handle | ||
596 | *sched, int af_family, | ||
597 | const struct sockaddr *serv_addr, | ||
598 | socklen_t addrlen, size_t maxbuf) | ||
599 | { | ||
600 | int s; | ||
601 | |||
602 | s = SOCKET (af_family, SOCK_STREAM, 0); | ||
603 | if (s == -1) | ||
604 | { | ||
605 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING | | ||
606 | GNUNET_ERROR_TYPE_BULK, "socket"); | ||
607 | return NULL; | ||
608 | } | ||
609 | if (0 != fcntl (s, F_SETFD, fcntl (s, F_GETFD) | FD_CLOEXEC)) | ||
610 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
611 | "fcntl"); | ||
612 | if (GNUNET_SYSERR == socket_set_blocking (s, GNUNET_NO)) | ||
613 | { | ||
614 | /* we'll treat this one as fatal */ | ||
615 | GNUNET_break (0 == CLOSE (s)); | ||
616 | return NULL; | ||
617 | } | ||
618 | if ((0 != CONNECT (s, serv_addr, addrlen)) && (errno != EINPROGRESS)) | ||
619 | { | ||
620 | /* maybe refused / unsupported address, try next */ | ||
621 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "connect"); | ||
622 | GNUNET_break (0 == CLOSE (s)); | ||
623 | return NULL; | ||
624 | } | ||
625 | return GNUNET_NETWORK_socket_create_from_existing (sched, s, maxbuf); | ||
626 | } | ||
627 | |||
628 | |||
629 | /** | ||
630 | * Check if socket is valid (no fatal errors have happened so far). | ||
631 | * Note that a socket that is still trying to connect is considered | ||
632 | * valid. | ||
633 | * | ||
634 | * @param sock socket to check | ||
635 | * @return GNUNET_YES if valid, GNUNET_NO otherwise | ||
636 | */ | ||
637 | int | ||
638 | GNUNET_NETWORK_socket_check (struct GNUNET_NETWORK_SocketHandle *sock) | ||
639 | { | ||
640 | if (sock->ai != NULL) | ||
641 | return GNUNET_YES; /* still trying to connect */ | ||
642 | return (sock->sock == -1) ? GNUNET_NO : GNUNET_YES; | ||
643 | } | ||
644 | |||
645 | |||
646 | /** | ||
647 | * Scheduler let us know that the connect task is finished (or was | ||
648 | * cancelled due to shutdown). Now really clean up. | ||
649 | */ | ||
650 | static void | ||
651 | destroy_continuation (void *cls, | ||
652 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
653 | { | ||
654 | struct GNUNET_NETWORK_SocketHandle *sock = cls; | ||
655 | GNUNET_NETWORK_TransmitReadyNotify notify; | ||
656 | |||
657 | if (sock->write_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) | ||
658 | { | ||
659 | #if DEBUG_NETWORK | ||
660 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
661 | "Destroy code waiting for writes to complete.\n"); | ||
662 | #endif | ||
663 | GNUNET_SCHEDULER_add_after (sock->sched, | ||
664 | GNUNET_YES, | ||
665 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
666 | sock->write_task, | ||
667 | &destroy_continuation, sock); | ||
668 | return; | ||
669 | } | ||
670 | if (sock->sock != -1) | ||
671 | { | ||
672 | #if DEBUG_NETWORK | ||
673 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down socket.\n"); | ||
674 | #endif | ||
675 | SHUTDOWN (sock->sock, SHUT_RDWR); | ||
676 | } | ||
677 | if (sock->read_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) | ||
678 | { | ||
679 | #if DEBUG_NETWORK | ||
680 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
681 | "Destroy code waiting for receive to complete.\n"); | ||
682 | #endif | ||
683 | GNUNET_SCHEDULER_add_after (sock->sched, | ||
684 | GNUNET_YES, | ||
685 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
686 | sock->read_task, | ||
687 | &destroy_continuation, sock); | ||
688 | return; | ||
689 | } | ||
690 | if (NULL != (notify = sock->nth.notify_ready)) | ||
691 | { | ||
692 | sock->nth.notify_ready = NULL; | ||
693 | notify (sock->nth.notify_ready_cls, 0, NULL); | ||
694 | if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) | ||
695 | { | ||
696 | GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task); | ||
697 | sock->nth.timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
698 | } | ||
699 | } | ||
700 | if (sock->sock != -1) | ||
701 | { | ||
702 | #if DEBUG_NETWORK | ||
703 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Closing socket.\n"); | ||
704 | #endif | ||
705 | GNUNET_break (0 == CLOSE (sock->sock)); | ||
706 | } | ||
707 | GNUNET_free_non_null (sock->addr); | ||
708 | if (sock->ai != NULL) | ||
709 | freeaddrinfo (sock->ai); | ||
710 | GNUNET_free (sock); | ||
711 | } | ||
712 | |||
713 | |||
714 | /** | ||
715 | * Close the socket and free associated resources. Pending | ||
716 | * transmissions are simply dropped. A pending receive call will be | ||
717 | * called with an error code of "EPIPE". | ||
718 | * | ||
719 | * @param sock socket to destroy | ||
720 | */ | ||
721 | void | ||
722 | GNUNET_NETWORK_socket_destroy (struct GNUNET_NETWORK_SocketHandle *sock) | ||
723 | { | ||
724 | #if DEBUG_NETWORK | ||
725 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
726 | "Network asked to destroy socket %p\n", sock); | ||
727 | #endif | ||
728 | if (sock->write_buffer_off == 0) | ||
729 | sock->ai_pos = NULL; /* if we're still trying to connect and have | ||
730 | no message pending, stop trying! */ | ||
731 | GNUNET_assert (sock->sched != NULL); | ||
732 | GNUNET_SCHEDULER_add_after (sock->sched, | ||
733 | GNUNET_YES, | ||
734 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
735 | sock->connect_task, | ||
736 | &destroy_continuation, sock); | ||
737 | } | ||
738 | |||
739 | /** | ||
740 | * Tell the receiver callback that a timeout was reached. | ||
741 | */ | ||
742 | static void | ||
743 | signal_timeout (struct GNUNET_NETWORK_SocketHandle *sh) | ||
744 | { | ||
745 | GNUNET_NETWORK_Receiver receiver; | ||
746 | |||
747 | #if DEBUG_NETWORK | ||
748 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
749 | "Network signals timeout to receiver!\n"); | ||
750 | #endif | ||
751 | GNUNET_assert (NULL != (receiver = sh->receiver)); | ||
752 | sh->receiver = NULL; | ||
753 | receiver (sh->receiver_cls, NULL, 0, NULL, 0, 0); | ||
754 | } | ||
755 | |||
756 | |||
757 | /** | ||
758 | * Tell the receiver callback that we had an IO error. | ||
759 | */ | ||
760 | static void | ||
761 | signal_error (struct GNUNET_NETWORK_SocketHandle *sh, int errcode) | ||
762 | { | ||
763 | GNUNET_NETWORK_Receiver receiver; | ||
764 | GNUNET_assert (NULL != (receiver = sh->receiver)); | ||
765 | sh->receiver = NULL; | ||
766 | receiver (sh->receiver_cls, NULL, 0, sh->addr, sh->addrlen, errcode); | ||
767 | } | ||
768 | |||
769 | |||
770 | /** | ||
771 | * This function is called once we either timeout | ||
772 | * or have data ready to read. | ||
773 | */ | ||
774 | static void | ||
775 | receive_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
776 | { | ||
777 | struct GNUNET_NETWORK_SocketHandle *sh = cls; | ||
778 | struct GNUNET_TIME_Absolute now; | ||
779 | char buffer[sh->max]; | ||
780 | ssize_t ret; | ||
781 | GNUNET_NETWORK_Receiver receiver; | ||
782 | |||
783 | sh->read_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
784 | now = GNUNET_TIME_absolute_get (); | ||
785 | if ((now.value > sh->receive_timeout.value) || | ||
786 | (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) || | ||
787 | (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))) | ||
788 | { | ||
789 | #if DEBUG_NETWORK | ||
790 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
791 | "Receive encounters error: timeout...\n"); | ||
792 | #endif | ||
793 | signal_timeout (sh); | ||
794 | return; | ||
795 | } | ||
796 | if (sh->sock == -1) | ||
797 | { | ||
798 | /* connect failed for good */ | ||
799 | #if DEBUG_NETWORK | ||
800 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
801 | "Receive encounters error, socket closed...\n"); | ||
802 | #endif | ||
803 | signal_error (sh, ECONNREFUSED); | ||
804 | return; | ||
805 | } | ||
806 | GNUNET_assert (FD_ISSET (sh->sock, tc->read_ready)); | ||
807 | RETRY: | ||
808 | ret = RECV (sh->sock, buffer, sh->max, MSG_DONTWAIT); | ||
809 | if (ret == -1) | ||
810 | { | ||
811 | if (errno == EINTR) | ||
812 | goto RETRY; | ||
813 | #if DEBUG_NETWORK | ||
814 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
815 | "Error receiving: %s\n", STRERROR (errno)); | ||
816 | #endif | ||
817 | signal_error (sh, errno); | ||
818 | return; | ||
819 | } | ||
820 | #if DEBUG_NETWORK | ||
821 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
822 | "Receive got %d bytes from OS!\n", ret); | ||
823 | #endif | ||
824 | GNUNET_assert (NULL != (receiver = sh->receiver)); | ||
825 | sh->receiver = NULL; | ||
826 | receiver (sh->receiver_cls, buffer, ret, sh->addr, sh->addrlen, 0); | ||
827 | } | ||
828 | |||
829 | |||
830 | /** | ||
831 | * This function is called after establishing a connection either has | ||
832 | * succeeded or timed out. Note that it is possible that the attempt | ||
833 | * timed out and that we're immediately retrying. If we are retrying, | ||
834 | * we need to wait again (or timeout); if we succeeded, we need to | ||
835 | * wait for data (or timeout). | ||
836 | */ | ||
837 | static void | ||
838 | receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
839 | { | ||
840 | struct GNUNET_NETWORK_SocketHandle *sh = cls; | ||
841 | struct GNUNET_TIME_Absolute now; | ||
842 | |||
843 | sh->read_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
844 | if ((sh->sock == -1) && | ||
845 | (sh->connect_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)) | ||
846 | { | ||
847 | /* not connected and no longer trying */ | ||
848 | #if DEBUG_NETWORK | ||
849 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
850 | "Receive encounters error, socket closed...\n"); | ||
851 | #endif | ||
852 | signal_error (sh, ECONNREFUSED); | ||
853 | return; | ||
854 | } | ||
855 | now = GNUNET_TIME_absolute_get (); | ||
856 | if ((now.value > sh->receive_timeout.value) || | ||
857 | (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))) | ||
858 | { | ||
859 | #if DEBUG_NETWORK | ||
860 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
861 | "Receive encounters error: timeout...\n"); | ||
862 | #endif | ||
863 | signal_timeout (sh); | ||
864 | return; | ||
865 | } | ||
866 | if (sh->connect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) | ||
867 | { | ||
868 | /* connect was retried */ | ||
869 | #if DEBUG_NETWORK | ||
870 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
871 | "Receive still waits on connect...\n"); | ||
872 | #endif | ||
873 | sh->read_task = GNUNET_SCHEDULER_add_after (tc->sched, | ||
874 | GNUNET_YES, | ||
875 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
876 | sh->connect_task, | ||
877 | &receive_again, sh); | ||
878 | } | ||
879 | else | ||
880 | { | ||
881 | /* connect succeeded, wait for data! */ | ||
882 | #if DEBUG_NETWORK | ||
883 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
884 | "Receive now waits for socket...\n"); | ||
885 | #endif | ||
886 | sh->read_task = GNUNET_SCHEDULER_add_read (tc->sched, | ||
887 | GNUNET_YES, | ||
888 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
889 | sh->connect_task, | ||
890 | GNUNET_TIME_absolute_get_remaining | ||
891 | (sh->receive_timeout), | ||
892 | sh->sock, &receive_ready, | ||
893 | sh); | ||
894 | } | ||
895 | } | ||
896 | |||
897 | |||
898 | /** | ||
899 | * Receive data from the given socket. Note that this function will | ||
900 | * call "receiver" asynchronously using the scheduler. It will | ||
901 | * "immediately" return. Note that there MUST only be one active | ||
902 | * receive call per socket at any given point in time (so do not | ||
903 | * call receive again until the receiver callback has been invoked). | ||
904 | * | ||
905 | * @param sched scheduler to use | ||
906 | * @param sock socket handle | ||
907 | * @param max maximum number of bytes to read | ||
908 | * @param timeout maximum amount of time to wait (use -1 for "forever") | ||
909 | * @param receiver function to call with received data | ||
910 | * @param receiver_cls closure for receiver | ||
911 | * @return scheduler task ID used for receiving, GNUNET_SCHEDULER_NO_PREREQUISITE_TASK on error | ||
912 | */ | ||
913 | GNUNET_SCHEDULER_TaskIdentifier | ||
914 | GNUNET_NETWORK_receive (struct GNUNET_NETWORK_SocketHandle *sock, | ||
915 | size_t max, | ||
916 | struct GNUNET_TIME_Relative timeout, | ||
917 | GNUNET_NETWORK_Receiver receiver, void *receiver_cls) | ||
918 | { | ||
919 | struct GNUNET_SCHEDULER_TaskContext tc; | ||
920 | #if DEBUG_NETWORK | ||
921 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
922 | "Network asked to receive from socket...\n"); | ||
923 | #endif | ||
924 | GNUNET_assert ((sock->read_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) && | ||
925 | (sock->receiver == NULL)); | ||
926 | sock->receiver = receiver; | ||
927 | sock->receiver_cls = receiver_cls; | ||
928 | sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
929 | sock->max = max; | ||
930 | memset (&tc, 0, sizeof (tc)); | ||
931 | tc.sched = sock->sched; | ||
932 | tc.reason = GNUNET_SCHEDULER_REASON_PREREQ_DONE; | ||
933 | receive_again (sock, &tc); | ||
934 | return sock->read_task; | ||
935 | } | ||
936 | |||
937 | |||
938 | /** | ||
939 | * Cancel receive job on the given socket. Note that the | ||
940 | * receiver callback must not have been called yet in order | ||
941 | * for the cancellation to be valid. | ||
942 | * | ||
943 | * @param sock socket handle | ||
944 | * @param task task identifier returned from the receive call | ||
945 | * @return closure of the original receiver callback | ||
946 | */ | ||
947 | void * | ||
948 | GNUNET_NETWORK_receive_cancel (struct GNUNET_NETWORK_SocketHandle *sock, | ||
949 | GNUNET_SCHEDULER_TaskIdentifier task) | ||
950 | { | ||
951 | GNUNET_assert (sock->read_task == task); | ||
952 | GNUNET_assert (sock == GNUNET_SCHEDULER_cancel (sock->sched, task)); | ||
953 | sock->read_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
954 | sock->receiver = NULL; | ||
955 | return sock->receiver_cls; | ||
956 | } | ||
957 | |||
958 | |||
959 | /** | ||
960 | * Try to call the transmit notify method (check if we do | ||
961 | * have enough space available first)! | ||
962 | * | ||
963 | * @param sock socket for which we should do this processing | ||
964 | * @return GNUNET_YES if we were able to call notify | ||
965 | */ | ||
966 | static int | ||
967 | process_notify (struct GNUNET_NETWORK_SocketHandle *sock) | ||
968 | { | ||
969 | size_t used; | ||
970 | size_t avail; | ||
971 | size_t size; | ||
972 | GNUNET_NETWORK_TransmitReadyNotify notify; | ||
973 | |||
974 | GNUNET_assert (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); | ||
975 | if (NULL == (notify = sock->nth.notify_ready)) | ||
976 | return GNUNET_NO; | ||
977 | used = sock->write_buffer_off - sock->write_buffer_pos; | ||
978 | avail = sock->write_buffer_size - used; | ||
979 | size = sock->nth.notify_size; | ||
980 | if (sock->nth.notify_size > avail) | ||
981 | return GNUNET_NO; | ||
982 | sock->nth.notify_ready = NULL; | ||
983 | if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) | ||
984 | { | ||
985 | GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task); | ||
986 | sock->nth.timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
987 | } | ||
988 | if (sock->write_buffer_size - sock->write_buffer_off < size) | ||
989 | { | ||
990 | /* need to compact */ | ||
991 | memmove (sock->write_buffer, | ||
992 | &sock->write_buffer[sock->write_buffer_pos], used); | ||
993 | sock->write_buffer_off -= sock->write_buffer_pos; | ||
994 | sock->write_buffer_pos = 0; | ||
995 | } | ||
996 | GNUNET_assert (sock->write_buffer_size - sock->write_buffer_off >= size); | ||
997 | size = notify (sock->nth.notify_ready_cls, | ||
998 | sock->write_buffer_size - sock->write_buffer_off, | ||
999 | &sock->write_buffer[sock->write_buffer_off]); | ||
1000 | sock->write_buffer_off += size; | ||
1001 | return GNUNET_YES; | ||
1002 | } | ||
1003 | |||
1004 | |||
1005 | /** | ||
1006 | * Task invoked by the scheduler when a call to transmit | ||
1007 | * is timing out (we never got enough buffer space to call | ||
1008 | * the callback function before the specified timeout | ||
1009 | * expired). | ||
1010 | * | ||
1011 | * This task notifies the client about the timeout. | ||
1012 | */ | ||
1013 | static void | ||
1014 | transmit_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
1015 | { | ||
1016 | struct GNUNET_NETWORK_SocketHandle *sock = cls; | ||
1017 | GNUNET_NETWORK_TransmitReadyNotify notify; | ||
1018 | |||
1019 | #if DEBUG_NETWORK | ||
1020 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmit fails, time out reached.\n"); | ||
1021 | #endif | ||
1022 | notify = sock->nth.notify_ready; | ||
1023 | sock->nth.notify_ready = NULL; | ||
1024 | notify (sock->nth.notify_ready_cls, 0, NULL); | ||
1025 | } | ||
1026 | |||
1027 | |||
1028 | static void | ||
1029 | transmit_error (struct GNUNET_NETWORK_SocketHandle *sock) | ||
1030 | { | ||
1031 | if (sock->nth.notify_ready == NULL) | ||
1032 | return; /* nobody to tell about it */ | ||
1033 | if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) | ||
1034 | { | ||
1035 | GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task); | ||
1036 | sock->nth.timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
1037 | } | ||
1038 | transmit_timeout (sock, NULL); | ||
1039 | } | ||
1040 | |||
1041 | |||
1042 | /** | ||
1043 | * See if we are now connected. If not, wait longer for | ||
1044 | * connect to succeed. If connected, we should be able | ||
1045 | * to write now as well, unless we timed out. | ||
1046 | */ | ||
1047 | static void | ||
1048 | transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
1049 | { | ||
1050 | struct GNUNET_NETWORK_SocketHandle *sock = cls; | ||
1051 | ssize_t ret; | ||
1052 | size_t have; | ||
1053 | |||
1054 | #if DEBUG_NETWORK | ||
1055 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1056 | "Transmit ready called --- will try to send\n"); | ||
1057 | #endif | ||
1058 | GNUNET_assert (sock->write_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); | ||
1059 | sock->write_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
1060 | if (sock->connect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) | ||
1061 | { | ||
1062 | /* still waiting for connect */ | ||
1063 | #if DEBUG_NETWORK | ||
1064 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1065 | "Transmission still waiting for connect...\n"); | ||
1066 | #endif | ||
1067 | GNUNET_assert (sock->write_task == | ||
1068 | GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); | ||
1069 | sock->write_task = | ||
1070 | GNUNET_SCHEDULER_add_delayed (tc->sched, GNUNET_NO, | ||
1071 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
1072 | sock->connect_task, | ||
1073 | GNUNET_TIME_UNIT_ZERO, &transmit_ready, | ||
1074 | sock); | ||
1075 | return; | ||
1076 | } | ||
1077 | if (sock->sock == -1) | ||
1078 | { | ||
1079 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1080 | _ | ||
1081 | ("Did not transmit request, socket closed or connect failed.\n")); | ||
1082 | transmit_error (sock); | ||
1083 | return; /* connect failed for good, we're finished */ | ||
1084 | } | ||
1085 | if ((tc->write_ready == NULL) || (!FD_ISSET (sock->sock, tc->write_ready))) | ||
1086 | { | ||
1087 | #if DEBUG_NETWORK | ||
1088 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1089 | "Socket not yet ready for writing, will wait for that.\n"); | ||
1090 | #endif | ||
1091 | goto SCHEDULE_WRITE; | ||
1092 | } | ||
1093 | GNUNET_assert (sock->write_buffer_off >= sock->write_buffer_pos); | ||
1094 | process_notify (sock); | ||
1095 | have = sock->write_buffer_off - sock->write_buffer_pos; | ||
1096 | if (have == 0) | ||
1097 | { | ||
1098 | #if DEBUG_NETWORK | ||
1099 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No data ready for writing.\n"); | ||
1100 | #endif | ||
1101 | return; | ||
1102 | } | ||
1103 | RETRY: | ||
1104 | ret = SEND (sock->sock, | ||
1105 | &sock->write_buffer[sock->write_buffer_pos], | ||
1106 | have, MSG_DONTWAIT | MSG_NOSIGNAL); | ||
1107 | if (ret == -1) | ||
1108 | { | ||
1109 | if (errno == EINTR) | ||
1110 | goto RETRY; | ||
1111 | #if DEBUG_NETWORK | ||
1112 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send"); | ||
1113 | #endif | ||
1114 | SHUTDOWN (sock->sock, SHUT_RDWR); | ||
1115 | GNUNET_break (0 == CLOSE (sock->sock)); | ||
1116 | sock->sock = -1; | ||
1117 | transmit_error (sock); | ||
1118 | return; | ||
1119 | } | ||
1120 | #if DEBUG_NETWORK | ||
1121 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitted %d bytes to OS\n", ret); | ||
1122 | #endif | ||
1123 | sock->write_buffer_pos += ret; | ||
1124 | if (sock->write_buffer_pos == sock->write_buffer_off) | ||
1125 | { | ||
1126 | /* transmitted all pending data */ | ||
1127 | sock->write_buffer_pos = 0; | ||
1128 | sock->write_buffer_off = 0; | ||
1129 | #if DEBUG_NETWORK | ||
1130 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1131 | "Transmission buffer now empty.\n", ret); | ||
1132 | #endif | ||
1133 | } | ||
1134 | if ((sock->write_buffer_off == 0) && (NULL == sock->nth.notify_ready)) | ||
1135 | return; /* all data sent! */ | ||
1136 | /* not done writing, schedule more */ | ||
1137 | #if DEBUG_NETWORK | ||
1138 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1139 | "More data ready for transmission, scheduling task again!\n"); | ||
1140 | #endif | ||
1141 | SCHEDULE_WRITE: | ||
1142 | if (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) | ||
1143 | sock->write_task = | ||
1144 | GNUNET_SCHEDULER_add_write (tc->sched, | ||
1145 | GNUNET_NO, | ||
1146 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
1147 | GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, | ||
1148 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
1149 | sock->sock, &transmit_ready, sock); | ||
1150 | } | ||
1151 | |||
1152 | |||
1153 | /** | ||
1154 | * Ask the socket to call us once the specified number of bytes | ||
1155 | * are free in the transmission buffer. May call the notify | ||
1156 | * method immediately if enough space is available. | ||
1157 | * | ||
1158 | * @param sock socket | ||
1159 | * @param size number of bytes to send | ||
1160 | * @param timeout after how long should we give up (and call | ||
1161 | * notify with buf NULL and size 0)? | ||
1162 | * @param notify function to call | ||
1163 | * @param notify_cls closure for notify | ||
1164 | * @return non-NULL if the notify callback was queued, | ||
1165 | * NULL if we are already going to notify someone else (busy) | ||
1166 | */ | ||
1167 | struct GNUNET_NETWORK_TransmitHandle * | ||
1168 | GNUNET_NETWORK_notify_transmit_ready (struct GNUNET_NETWORK_SocketHandle | ||
1169 | *sock, size_t size, | ||
1170 | struct GNUNET_TIME_Relative timeout, | ||
1171 | GNUNET_NETWORK_TransmitReadyNotify | ||
1172 | notify, void *notify_cls) | ||
1173 | { | ||
1174 | if (sock->nth.notify_ready != NULL) | ||
1175 | return NULL; | ||
1176 | GNUNET_assert (notify != NULL); | ||
1177 | GNUNET_assert (sock->write_buffer_size >= size); | ||
1178 | |||
1179 | if ((sock->sock == -1) && | ||
1180 | (sock->connect_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)) | ||
1181 | { | ||
1182 | #if DEBUG_NETWORK | ||
1183 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1184 | "Transmit fails, connection failed.\n"); | ||
1185 | #endif | ||
1186 | notify (notify_cls, 0, NULL); | ||
1187 | return &sock->nth; | ||
1188 | } | ||
1189 | GNUNET_assert (sock->write_buffer_off <= sock->write_buffer_size); | ||
1190 | GNUNET_assert (sock->write_buffer_pos <= sock->write_buffer_size); | ||
1191 | GNUNET_assert (sock->write_buffer_pos <= sock->write_buffer_off); | ||
1192 | sock->nth.notify_ready = notify; | ||
1193 | sock->nth.notify_ready_cls = notify_cls; | ||
1194 | sock->nth.sh = sock; | ||
1195 | sock->nth.notify_size = size; | ||
1196 | sock->nth.transmit_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
1197 | sock->nth.timeout_task = GNUNET_SCHEDULER_add_delayed (sock->sched, | ||
1198 | GNUNET_NO, | ||
1199 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
1200 | GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, | ||
1201 | timeout, | ||
1202 | &transmit_timeout, | ||
1203 | sock); | ||
1204 | #if DEBUG_NETWORK | ||
1205 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1206 | "Scheduling asynchronous transmission once connect is done...\n"); | ||
1207 | #endif | ||
1208 | if (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) | ||
1209 | sock->write_task = GNUNET_SCHEDULER_add_delayed (sock->sched, | ||
1210 | GNUNET_NO, | ||
1211 | GNUNET_SCHEDULER_PRIORITY_KEEP, | ||
1212 | sock->connect_task, | ||
1213 | GNUNET_TIME_UNIT_ZERO, | ||
1214 | &transmit_ready, sock); | ||
1215 | return &sock->nth; | ||
1216 | } | ||
1217 | |||
1218 | |||
1219 | /** | ||
1220 | * Cancel the specified transmission-ready | ||
1221 | * notification. | ||
1222 | */ | ||
1223 | void | ||
1224 | GNUNET_NETWORK_notify_transmit_ready_cancel (struct | ||
1225 | GNUNET_NETWORK_TransmitHandle *h) | ||
1226 | { | ||
1227 | GNUNET_assert (h->notify_ready != NULL); | ||
1228 | GNUNET_SCHEDULER_cancel (h->sh->sched, h->timeout_task); | ||
1229 | h->timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
1230 | h->notify_ready = NULL; | ||
1231 | } | ||
1232 | |||
1233 | |||
1234 | #if 0 /* keep Emacsens' auto-indent happy */ | ||
1235 | { | ||
1236 | #endif | ||
1237 | #ifdef __cplusplus | ||
1238 | } | ||
1239 | #endif | ||