Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
view src/gt_xfer.c @ 0:d39e1d0d75b6
initial add
author | paulo@hit-nxdomain.opendns.com |
---|---|
date | Sat, 20 Feb 2010 21:18:28 -0800 |
parents | |
children |
line source
1 /*
2 * $Id: gt_xfer.c,v 1.103 2005/01/05 14:08:40 mkern Exp $
3 *
4 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 */
17 #include "gt_gnutella.h"
19 #include "gt_xfer_obj.h"
20 #include "gt_xfer.h"
21 #include "gt_http_client.h"
22 #include "gt_http_server.h"
23 #include "gt_share.h"
24 #include "gt_share_file.h"
25 #include "gt_packet.h"
26 #include "gt_node.h"
27 #include "gt_node_list.h"
28 #include "gt_netorg.h"
29 #include "gt_connect.h"
30 #include "gt_bind.h"
31 #include "gt_utils.h"
33 #include "encoding/url.h"
35 #include "transfer/source.h"
37 /******************************************************************************/
39 /* maximum number of push connections in limbo each remote user */
40 #define PUSH_MAX_LIMBO gt_config_get_int("transfer/push_max_in_limbo=5")
42 /******************************************************************************/
44 /* an alternative to carrying the push TTL around in the source url */
45 #define PUSH_MAX_TTL 12
47 /* maximum time to keep push connections in "limbo" while awaiting giftd
48 * to reissue Chunks */
49 #define PUSH_LIMBO_TIMEOUT (4 * MINUTES)
51 /* how long to wait for a PUSH reply before timing out in order to free the
52 * Chunk */
53 #define PUSH_WAIT_INTERVAL (30 * SECONDS)
55 /* minimum interval between pushes */
56 #define PUSH_MIN_DEFER_TIME (30 * ESECONDS)
58 /* maximum amount of time a push will be forced to wait before being sent */
59 #define PUSH_MAX_DEFER_TIME (10 * EMINUTES)
61 /*****************************************************************************/
63 /* this stores information about an indirect ("pushed") download */
64 typedef struct gt_push_source
65 {
66 gt_guid_t *guid;
67 in_addr_t ip;
68 in_addr_t src_ip; /* whether this push was to a local source */
69 List *xfers; /* xfers for this source */
70 List *connections; /* connection for this source */
71 time_t last_sent; /* time of last push sent to this source */
72 double defer_time; /* min time to wait before sending another push
73 * doubles every push to PUSH_MAX_DEFER_TIME */
74 } GtPushSource;
76 /*****************************************************************************/
78 /* Maps guid->{list of unique GtPushSources} */
79 static Dataset *gt_push_requests;
81 /******************************************************************************/
83 static void push_source_reset_last_sent (GtPushSource *push_src);
84 static void push_source_set_last_sent (gt_guid_t *guid, in_addr_t ip);
85 static BOOL push_source_should_send (gt_guid_t *guid, in_addr_t ip);
87 /*****************************************************************************/
89 /*
90 * The source URL is stored on disk and could be outdated if the format has
91 * changed. This updates it with any changes when we first read it from
92 * the state file on startup.
93 */
94 static void replace_url (Source *src, GtSource *gt)
95 {
96 char *url;
98 if (!(url = gt_source_serialize (gt)))
99 return;
101 /* swap urls */
102 FREE (src->url);
103 src->url = url;
104 }
106 /******************************************************************************/
108 static FileShare *lookup_index (GtTransfer *xfer, char *request)
109 {
110 FileShare *file;
111 char *index_str;
112 char *filename;
113 char *decoded;
114 uint32_t index;
116 filename = request;
117 index_str = string_sep (&filename, "/");
119 if (!filename || !index_str)
120 return NULL;
122 index = ATOUL (index_str);
124 decoded = gt_url_decode (filename);
125 file = gt_share_local_lookup_by_index (index, decoded);
127 free (decoded);
129 /* the filename may or may not be url encoded */
130 if (!file)
131 file = gt_share_local_lookup_by_index (index, filename);
133 return file;
134 }
136 static FileShare *lookup_urns (GtTransfer *xfer, char *urns)
137 {
138 FileShare *file = NULL;
139 char *urn;
141 /*
142 * Try to lookup all urns provided in the header,
143 * until one is found.
144 */
145 while (!file && !string_isempty (urns))
146 {
147 urn = string_sep_set (&urns, ", ");
148 file = gt_share_local_lookup_by_urn (urn);
149 }
151 return file;
152 }
154 static FileShare *lookup_uri_res (GtTransfer *xfer, char *request)
155 {
156 FileShare *file = NULL;
157 char *resolver = NULL;
158 char *urn;
160 resolver = string_sep (&request, "?");
161 urn = string_sep (&request, " ");
163 if (resolver && !strcasecmp (resolver, "N2R"))
164 {
165 string_trim (request);
166 file = lookup_urns (xfer, urn);
167 }
169 if (file && HTTP_DEBUG)
170 GT->dbg (GT, "file=%s", share_get_hpath (file));
172 return file;
173 }
175 static Share *lookup_hpath (char *namespace, GtTransfer *xfer, char *request)
176 {
177 char *hpath;
178 Share *share;
180 /*
181 * Reconstruct the hpath
182 */
183 if (!(hpath = stringf_dup ("/%s/%s", namespace, request)))
184 return NULL;
186 if (HTTP_DEBUG)
187 GT->dbg (GT, "request by hpath: %s", hpath);
189 share = GT->share_lookup (GT, SHARE_LOOKUP_HPATH, hpath);
190 free (hpath);
192 return share;
193 }
195 /* Take a request for a file, i.e. everything after GET in:
196 *
197 * "GET /get/1279/filename.mp3"
198 *
199 * and convert it to a localized path to a file.
200 *
201 * Path has been "secured" already if necessary.
202 *
203 * The path returned must be static.
204 * TODO: this interface is a bit bizarre */
205 char *gt_localize_request (GtTransfer *xfer, char *s_path, int *authorized)
206 {
207 static char open_path[PATH_MAX];
208 char *namespace;
209 char *path, *path0;
210 char *content_urns;
211 FileShare *file;
213 /* TODO: use authorized for Browse Host (BH) requests */
214 if (!STRCMP (s_path, "/"))
215 {
216 /* *authorized = TRUE; */
217 if (HTTP_DEBUG)
218 GT->DBGFN (GT, "received unimplemented Browse Host request");
220 return NULL;
221 }
223 if (authorized)
224 *authorized = FALSE;
226 if (!(path0 = path = STRDUP (s_path)))
227 return NULL;
229 if (HTTP_DEBUG)
230 GT->dbg (GT, "path=%s", path);
232 /* get rid of leading slash */
233 string_sep (&path, "/");
234 namespace = string_sep (&path, "/");
236 if (!namespace || !path)
237 {
238 GT->DBGFN (GT, "null namespace or path: %s %s\n", namespace, path);
239 free (path0);
240 return NULL;
241 }
243 /*
244 * If the client supplied "X-Gnutella-Content-URN: ", lookup
245 * by that instead of the request.
246 */
247 content_urns = dataset_lookupstr (xfer->header, "x-gnutella-content-urn");
249 if (content_urns)
250 file = lookup_urns (xfer, content_urns);
251 else if (!strcasecmp (namespace, "get"))
252 file = lookup_index (xfer, path);
253 else if (!strcasecmp (namespace, "uri-res"))
254 file = lookup_uri_res (xfer, path);
255 else
256 file = lookup_hpath (namespace, xfer, path);
258 /*
259 * Set xfer->content_urn [which replies with 'X-Gnutella-Content-URN']
260 * to a comma-separated list of all URNs for this file.
261 */
262 xfer->content_urns = gt_share_local_get_urns (file);
264 if (!file)
265 {
266 if (HTTP_DEBUG)
267 GT->DBGFN (GT, "bad request: /%s/%s", namespace, path);
269 free (path0);
270 return NULL;
271 }
273 free (path0);
275 if (!share_complete (file))
276 return NULL;
278 /* argh, need to return static data */
279 snprintf (open_path, sizeof (open_path) - 1, "%s", share_get_hpath (file));
281 /* try to fill in the hash */
282 xfer->hash = share_dsp_hash (file, "SHA1");
284 return open_path;
285 }
287 /******************************************************************************/
289 static char *index_request (char *request, size_t size,
290 uint32_t index, const char *filename)
291 {
292 /*
293 * The filename may not have ever been set. Fail the request in
294 * this case.
295 */
296 if (!filename || string_isempty (filename))
297 return NULL;
299 /*
300 * Filename is encoded, we don't support sending unecoded requests
301 * anymore. NOTE: filename doesnt have leading '/' here, that may change
302 */
303 snprintf (request, size - 1, "/get/%u/%s", index, filename);
304 return request;
305 }
307 /*
308 * Setup a request string. Try request-by-hash (/uri-res/N2R?urn:sha1:..),
309 * but if there are any problems, fallback to a "/get/<index>/<filename>"
310 * request.
311 */
312 static char *request_str (Source *source, uint32_t index, char *filename)
313 {
314 static char request[RW_BUFFER];
315 char *hash = source->hash;
316 GtSource *gt;
318 gt = source->udata;
319 assert (gt != NULL);
321 /*
322 * Do a uri-res request unless one has failed already or
323 * if we have no filename and thus no choice but to use the hash.
324 * (doesn't happen currently but will for download-mesh sources
325 * that only have the hash and no filename)
326 */
327 if (hash && (!gt->uri_res_failed || string_isempty (filename)))
328 {
329 char *str0, *str;
331 if (!(str0 = STRDUP (hash)))
332 return index_request (request, sizeof (request), index, filename);
334 str = str0;
335 string_sep (&str, ":");
337 /* hashes are canonically uppercase on the gnet */
338 string_upper (str);
340 if (str)
341 {
342 snprintf (request, sizeof (request) - 1,
343 "/uri-res/N2R?urn:sha1:%s", str);
344 free (str0);
345 return request;
346 }
348 free (str0);
349 }
351 return index_request (request, sizeof (request), index, filename);
352 }
354 /*****************************************************************************/
355 /* PUSH HANDLING */
357 /*
358 * This code has to deal with some tricky race conditions involving
359 * chunk timeouts and pushes.
360 */
362 static GtPushSource *gt_push_source_new (gt_guid_t *guid, in_addr_t ip,
363 in_addr_t src_ip)
364 {
365 GtPushSource *src;
367 if (!(src = MALLOC (sizeof (GtPushSource))))
368 return NULL;
370 src->guid = gt_guid_dup (guid);
371 src->ip = ip;
372 src->src_ip = src_ip;
373 src->xfers = NULL;
374 src->connections = NULL;
376 push_source_reset_last_sent (src);
378 return src;
379 }
381 static void gt_push_source_free (GtPushSource *src)
382 {
383 if (!src)
384 return;
386 assert (src->xfers == NULL);
387 assert (src->connections == NULL);
389 free (src->guid);
390 free (src);
391 }
393 /*****************************************************************************/
395 /* TODO: break this into two parts, first part looks in the
396 * list for matching ip. If none is found, look for
397 * a firewalled (local ip) push source. */
398 static int find_ip (GtPushSource *src, in_addr_t *ip)
399 {
400 /* If the source is a local IP address behind a non-local one,
401 * authorize by just the client guid. Otherwise, use the IP. */
402 if (gt_is_local_ip (src->ip, src->src_ip) || src->ip == *ip)
403 return 0;
405 return -1;
406 }
408 static List *lookup_source_list (gt_guid_t *guid)
409 {
410 List *src_list;
412 if (!(src_list = dataset_lookup (gt_push_requests, guid, 16)))
413 return NULL;
415 return src_list;
416 }
418 static GtPushSource *push_source_lookup (gt_guid_t *guid, in_addr_t ip)
419 {
420 List *requests;
421 List *list;
423 if (!(requests = lookup_source_list (guid)))
424 return NULL;
426 list = list_find_custom (requests, &ip, (ListForeachFunc)find_ip);
427 return list_nth_data (list, 0);
428 }
430 /*****************************************************************************/
432 static void insert_source_list (gt_guid_t *guid, List *src_list)
433 {
434 if (!gt_push_requests)
435 gt_push_requests = dataset_new (DATASET_HASH);
437 dataset_insert (>_push_requests, guid, 16, src_list, 0);
438 }
440 static void add_push_source (List *pushes, gt_guid_t *guid, in_addr_t ip,
441 in_addr_t src_ip)
442 {
443 GtPushSource *src;
444 List *old_list;
446 if (!(src = gt_push_source_new (guid, ip, src_ip)))
447 return;
449 if ((old_list = list_find_custom (pushes, &ip, (ListForeachFunc)find_ip)))
450 {
451 /* push source is already there */
452 gt_push_source_free (src);
453 return;
454 }
456 pushes = list_prepend (pushes, src);
457 insert_source_list (guid, pushes);
458 }
460 void gt_push_source_add (gt_guid_t *guid, in_addr_t ip, in_addr_t src_ip)
461 {
462 List *pushes;
464 pushes = lookup_source_list (guid);
465 add_push_source (pushes, guid, ip, src_ip);
466 }
468 /*****************************************************************************/
469 /* Timing controls for push requests */
471 static void push_source_reset_last_sent (GtPushSource *push_src)
472 {
473 push_src->last_sent = gt_uptime (); /* wrong */
474 push_src->defer_time = 0.0;
475 }
477 static void push_source_set_last_sent (gt_guid_t *guid, in_addr_t ip)
478 {
479 GtPushSource *src;
481 if (!(src = push_source_lookup (guid, ip)))
482 return;
484 time (&src->last_sent);
485 }
487 static BOOL push_source_should_send (gt_guid_t *guid, in_addr_t ip)
488 {
489 GtPushSource *src;
490 double deferred;
491 time_t now;
493 time (&now);
495 if (!(src = push_source_lookup (guid, ip)))
496 return FALSE;
498 deferred = difftime (now, src->last_sent);
500 /* randomize the defer time a bit in order to not send pushes for all
501 * downloads at once */
502 if (deferred < src->defer_time + -10.0 + 20.0 * rand() / (RAND_MAX + 1.0))
503 return FALSE;
505 /*
506 * Double the defer time interval (the minimum time between sent
507 * pushes for this source).
508 */
509 src->defer_time *= 2;
510 if (src->defer_time >= PUSH_MAX_DEFER_TIME)
511 src->defer_time = PUSH_MAX_DEFER_TIME;
513 if (src->defer_time == 0)
514 src->defer_time = PUSH_MIN_DEFER_TIME;
516 return TRUE;
517 }
519 /*****************************************************************************/
521 static void flush_inputs (TCPC *c)
522 {
523 int ret;
525 assert (c->fd >= 0);
527 /* queued writes arent used by the HTTP system in this plugin,
528 * so this should always be true */
529 ret = tcp_flush (c, TRUE);
530 assert (ret == 0);
532 input_remove_all (c->fd);
533 }
535 static void continue_download (GtPushSource *push_src, GtTransfer *xfer,
536 TCPC *c)
537 {
538 Chunk *chunk;
540 chunk = gt_transfer_get_chunk (xfer);
542 /* remove all previous inputs */
543 flush_inputs (c);
545 /* HACK: remove the detach timeout */
546 timer_remove_zero (&xfer->detach_timer);
548 /* connect the TCPC and GtTransfer */
549 gt_transfer_set_tcpc (xfer, c);
551 /* update the IP and port for placing in Host: */
552 peer_addr (c->fd, &xfer->ip, &xfer->port);
554 /* the connection and the chunk have met up */
555 gt_transfer_status (xfer, SOURCE_WAITING, "Received GIV response");
557 if (HTTP_DEBUG)
558 GT->DBGSOCK (GT, c, "Continuing download for %s", xfer->request);
560 input_add (c->fd, xfer, INPUT_WRITE,
561 (InputCallback)gt_http_client_start, TIMEOUT_DEF);
562 }
564 static void reset_conn (int fd, input_id id, TCPC *c)
565 {
566 /*
567 * We should only get here if some data was sent, or if it timed out. In
568 * which case we should close this connection, because it shouldn't be
569 * sending anything.
570 */
571 if (HTTP_DEBUG)
572 {
573 if (fd == -1)
574 GT->DBGSOCK (GT, c, "connection timed out");
575 else
576 GT->DBGSOCK (GT, c, "connection closed or sent invalid data");
577 }
579 gt_push_source_remove_conn (c);
580 tcp_close (c);
581 }
583 static void store_conn (GtPushSource *src, TCPC *c)
584 {
585 flush_inputs (c);
587 input_add (c->fd, c, INPUT_READ,
588 (InputCallback)reset_conn, PUSH_LIMBO_TIMEOUT);
590 assert (!list_find (src->connections, c));
591 src->connections = list_prepend (src->connections, c);
593 if (HTTP_DEBUG)
594 GT->DBGSOCK (GT, c, "storing connection");
595 }
597 BOOL gt_push_source_add_conn (gt_guid_t *guid, in_addr_t ip, TCPC *c)
598 {
599 GtTransfer *xfer;
600 GtPushSource *push_src;
602 if (!(push_src = push_source_lookup (guid, ip)))
603 {
604 if (HTTP_DEBUG)
605 {
606 GT->err (GT, "couldn't find push source %s:[%s]",
607 gt_guid_str (guid), net_ip_str (ip));
608 }
610 tcp_close (c);
611 return FALSE;
612 }
614 /*
615 * Don't allow too many connections in flight from the same remote user.
616 */
617 if (list_length (push_src->connections) >= PUSH_MAX_LIMBO)
618 {
619 if (HTTP_DEBUG)
620 {
621 GT->DBGSOCK (GT, c, "too many push connections from %s, closing",
622 gt_guid_str (guid));
623 }
625 tcp_close (c);
626 return FALSE;
627 }
629 /*
630 * Since we now know this push source is alive, reset the push send
631 * tracking time: in case the connection is lost, we'll resend the push
632 * right away instead of waiting.
633 */
634 push_source_reset_last_sent (push_src);
636 /*
637 * Store the connection if there are no GtTransfer requests from
638 * giFT at the moment.
639 */
640 if (!push_src->xfers)
641 {
642 store_conn (push_src, c);
643 return FALSE;
644 }
646 xfer = list_nth_data (push_src->xfers, 0);
647 push_src->xfers = list_remove (push_src->xfers, xfer);
649 continue_download (push_src, xfer, c);
650 return TRUE;
651 }
653 /* return TRUE if there's a connection residing on this push source */
654 static BOOL push_source_lookup_conn (gt_guid_t *guid, in_addr_t ip)
655 {
656 GtPushSource *push_src;
658 if (!(push_src = push_source_lookup (guid, ip)))
659 return FALSE;
661 if (push_src->connections)
662 {
663 if (HTTP_DEBUG)
664 GT->DBGFN (GT, "found push connection for %s", net_ip_str (ip));
666 return TRUE;
667 }
669 return FALSE;
670 }
672 static void store_xfer (GtPushSource *src, GtTransfer *xfer)
673 {
674 assert (!list_find (src->xfers, xfer));
675 src->xfers = list_prepend (src->xfers, xfer);
676 }
678 BOOL gt_push_source_add_xfer (gt_guid_t *guid, in_addr_t ip,
679 in_addr_t src_ip, GtTransfer *xfer)
680 {
681 TCPC *c;
682 GtPushSource *push_src;
684 assert (xfer != NULL);
686 /* create the source if it doesn't exist already */
687 gt_push_source_add (guid, ip, src_ip);
689 if (!(push_src = push_source_lookup (guid, ip)))
690 {
691 if (HTTP_DEBUG)
692 {
693 GT->err (GT, "couldn't find push source (%s:[%s]) for chunk %s",
694 gt_guid_str (guid), net_ip_str (ip), xfer->request);
695 }
697 return FALSE;
698 }
700 /*
701 * Store the GtTransfer if there are no connections to service it
702 * at the moment.
703 */
704 if (!push_src->connections)
705 {
706 store_xfer (push_src, xfer);
707 return FALSE;
708 }
710 c = list_nth_data (push_src->connections, 0);
711 push_src->connections = list_remove (push_src->connections, c);
713 continue_download (push_src, xfer, c);
714 return TRUE;
715 }
717 /*****************************************************************************/
719 static BOOL remove_xfer (GtPushSource *src, GtTransfer *xfer)
720 {
721 src->xfers = list_remove (src->xfers, xfer);
722 return FALSE;
723 }
725 static void remove_xfer_list (ds_data_t *key, ds_data_t *value,
726 GtTransfer *xfer)
727 {
728 List *src_list = value->data;
730 list_foreach (src_list, (ListForeachFunc)remove_xfer, xfer);
731 }
733 /*
734 * The chunk is being cancelled, so remove it from being tracked.
735 *
736 * After this, if the push recipient connects, we will have to wait
737 * for another chunk timeout before transmitting.
738 */
739 void gt_push_source_remove_xfer (GtTransfer *xfer)
740 {
741 if (!xfer)
742 return;
744 dataset_foreach (gt_push_requests, DS_FOREACH(remove_xfer_list), xfer);
745 }
747 static BOOL remove_conn (GtPushSource *src, TCPC *c)
748 {
749 src->connections = list_remove (src->connections, c);
750 return FALSE;
751 }
753 static void remove_conn_list (ds_data_t *key, ds_data_t *value, TCPC *c)
754 {
755 List *src_list = value->data;
757 list_foreach (src_list, (ListForeachFunc)remove_conn, c);
758 }
760 /*
761 * The connection from this push download closed
762 */
763 void gt_push_source_remove_conn (TCPC *c)
764 {
765 if (!c)
766 return;
768 dataset_foreach (gt_push_requests, DS_FOREACH(remove_conn_list), c);
769 }
771 static BOOL cleanup_xfer (GtTransfer *xfer, void *udata)
772 {
773 gt_push_source_remove_xfer (xfer);
774 return TRUE;
775 }
777 static BOOL cleanup_conn (TCPC *c, void *udata)
778 {
779 gt_push_source_remove_conn (c);
780 tcp_close (c);
781 return TRUE;
782 }
784 static void remove_push_source (GtPushSource *src)
785 {
786 List *src_list;
788 src_list = lookup_source_list (src->guid);
789 src_list = list_remove (src_list, src);
791 insert_source_list (src->guid, src_list);
792 }
794 void gt_push_source_remove (gt_guid_t *guid, in_addr_t ip, in_addr_t src_ip)
795 {
796 GtPushSource *src;
798 if (!(src = push_source_lookup (guid, ip)))
799 return;
801 /* cleanup all the chunks and connections */
802 src->xfers =
803 list_foreach_remove (src->xfers, (ListForeachFunc)cleanup_xfer,
804 NULL);
805 src->connections =
806 list_foreach_remove (src->connections, (ListForeachFunc)cleanup_conn,
807 NULL);
809 /* remove this source from the global list */
810 remove_push_source (src);
812 gt_push_source_free (src);
813 }
815 /*****************************************************************************/
817 static BOOL detach_timeout (void *udata)
818 {
819 GtTransfer *xfer = udata;
821 /* Added this on 2004-12-22 to track observed assertion failure in
822 * gt_transfer_get_chunk. -- mkern
823 */
824 if (!xfer->chunk || xfer->chunk->udata != xfer)
825 {
826 GT->DBGFN (GT, "Detach timeout troubles. status = %d, "
827 "text = %s, xfer->ip = %s, "
828 "xfer = %p, xfer->chunk->udata = %p, "
829 "xfer->detach_timer = 0x%X",
830 xfer->detach_status, xfer->detach_msgtxt,
831 net_ip_str (xfer->ip), xfer,
832 xfer->chunk->udata, xfer->detach_timer);
833 }
835 /* Sometimes gt_transfer_status will trigger an
836 * assert (xfer->chunk->udata == xfer) failure in gt_transfer_get_chunk.
837 * But why? Is xfer already freed? Does it have another chunk and the
838 * timer was not removed?
839 */
840 gt_transfer_status (xfer, xfer->detach_status, xfer->detach_msgtxt);
841 gt_transfer_close (xfer, TRUE);
843 return FALSE;
844 }
846 /*
847 * Attach a timer that will "detach" the GtTransfer from the Chunk by
848 * cancelling it, but pretend that the Source is in some other state besides
849 * "cancelled" or "timed out" by changing the status text.
850 *
851 * This is useful to keep a semi-consistent UI in certain situations, such as
852 * sending out push requests, and cancelling requests when the remote side has
853 * queued our request.
854 */
855 static void detach_transfer_in (GtTransfer *xfer, SourceStatus status,
856 char *status_txt, time_t interval)
857 {
858 char *msg;
860 msg = STRDUP (status_txt);
862 gt_transfer_status (xfer, status, msg);
863 xfer->detach_status = status;
865 free (xfer->detach_msgtxt);
866 xfer->detach_msgtxt = msg;
868 xfer->detach_timer = timer_add (interval,
869 (TimerCallback)detach_timeout, xfer);
870 }
872 /*
873 * Detach an GtTransfer from its Chunk.
874 */
875 static void detach_transfer (GtTransfer *xfer, SourceStatus status,
876 char *msgtxt)
877 {
878 /*
879 * Cancelling from p->download_start will cause download_pause() to crash.
880 * So, the detach must happen from timer context.
881 */
882 detach_transfer_in (xfer, status, msgtxt, 2 * SECONDS);
883 }
885 /*****************************************************************************/
887 static void send_push (GtTransfer *xfer, GtSource *gt, TCPC *server)
888 {
889 GtPacket *packet;
891 if (!(packet = gt_packet_new (GT_MSG_PUSH, PUSH_MAX_TTL, NULL)))
892 return;
894 gt_packet_put_ustr (packet, gt->guid, 16);
895 gt_packet_put_uint32 (packet, gt->index);
896 gt_packet_put_ip (packet, GT_NODE(server)->my_ip);
897 gt_packet_put_port (packet, GT_SELF->gt_port);
899 if (gt_packet_error (packet))
900 {
901 gt_packet_free (packet);
902 return;
903 }
905 gt_packet_send (server, packet);
906 gt_packet_free (packet);
908 /*
909 * Don't wait for the whole Chunk timeout -- that keeps the Chunk
910 * occupied for too long if there are other active sources (the Chunk
911 * also times out longer and longer each time, so this gets worse
912 * the longer the transfer is inactive).
913 *
914 * This is really an infelicity of the Chunk system.
915 */
916 detach_transfer_in (xfer, SOURCE_QUEUED_REMOTE, "Sent PUSH request",
917 PUSH_WAIT_INTERVAL);
919 /* set the last time we sent a push to now */
920 push_source_set_last_sent (gt->guid, gt->user_ip);
921 }
923 static BOOL send_push_to_server (in_addr_t server_ip, in_port_t server_port,
924 GtTransfer *xfer, GtSource *gt)
925 {
926 GtNode *server;
928 if (!(server = gt_node_lookup (server_ip, server_port)))
929 {
930 server = gt_node_register (server_ip, server_port,
931 GT_NODE_ULTRA);
932 }
934 if (!server)
935 {
936 GT->err (GT, "couldn't register server");
937 return FALSE;
938 }
940 if (server->state & (GT_NODE_CONNECTED | GT_NODE_CONNECTING_2))
941 {
942 assert (GT_CONN(server) != NULL);
944 /* Server is in a state for receiving packets -- send the push */
945 send_push (xfer, gt, GT_CONN(server));
946 return TRUE;
947 }
948 else if (server->state & GT_NODE_CONNECTING_1)
949 {
950 /* dont try to connect again; wait till we're connected */
951 return FALSE;
952 }
953 else if (gt_conn_need_connections (GT_NODE_ULTRA) > 0 &&
954 !server->tried_connect &&
955 gt_connect (server) >= 0)
956 {
957 /*
958 * We've tried to connect to the server so we could deliver the push
959 * request eventually NOTE: this doesnt send a push until the next
960 * chunk timeout.
961 */
962 return FALSE;
963 }
965 return FALSE;
966 }
968 static void handle_push_download (Chunk *chunk, GtTransfer *xfer, GtSource *gt)
969 {
970 GtNode *server;
972 /*
973 * If this succeeds, we already have a connection to this
974 * user and the transfer will continue by using that connection.
975 *
976 * TODO: the gt_push_source_add() should be used by some
977 * per-source data structure
978 */
979 if (gt_push_source_add_xfer (gt->guid, gt->user_ip, gt->server_ip, xfer))
980 return;
982 /*
983 * Dont send pushes too often. Maybe should use a global queue instead.
984 *
985 * NOTE: we can't free the xfer here because we have stored it.
986 */
987 if (push_source_should_send (gt->guid, gt->user_ip) == FALSE)
988 {
989 /* don't occupy the Chunk forever */
990 detach_transfer_in (xfer, SOURCE_QUEUED_REMOTE, "Awaiting connection",
991 PUSH_WAIT_INTERVAL);
992 return;
993 }
995 /*
996 * Next, try to find the server that supplied this result,
997 * and send them a push.
998 */
999 if (send_push_to_server (gt->server_ip, gt->server_port, xfer, gt))
1000 return;
1002 /*
1003 * Finally, try sending to a random connected server.
1004 *
1005 * TODO: these should be rate-limited, either globally or
1006 * per-source.
1007 */
1008 if ((server = gt_conn_random (GT_NODE_ULTRA, GT_NODE_CONNECTED)))
1009 {
1010 send_push_to_server (server->ip, server->gt_port, xfer, gt);
1011 return;
1012 }
1014 detach_transfer (xfer, SOURCE_QUEUED_REMOTE, "No PUSH route");
1015 }
1017 static BOOL set_request (GtTransfer *xfer, Chunk *chunk, Source *source,
1018 GtSource *gt_src)
1019 {
1020 char *request;
1022 if (!chunk || !xfer)
1023 return FALSE;
1025 request = request_str (source, gt_src->index, gt_src->filename);
1027 if (!gt_transfer_set_request (xfer, request))
1028 {
1029 GT->DBGFN (GT, "UI made an invalid request for '%s'", request);
1030 return FALSE;
1031 }
1033 /* connect the xfer and the chunk */
1034 gt_transfer_set_chunk (xfer, chunk);
1036 return TRUE;
1037 }
1039 static BOOL should_push (GtSource *gt)
1040 {
1041 TCPC *persistent;
1043 /* we cannot push if there is no guid to send the push to */
1044 if (gt_guid_is_empty (gt->guid))
1045 return FALSE;
1047 persistent = gt_http_connection_lookup (GT_TRANSFER_DOWNLOAD,
1048 gt->user_ip,
1049 gt->user_port);
1051 /* need to close the connection to re-add it to the list, because
1052 * _lookup removes it from the persistent connection list */
1053 gt_http_connection_close (GT_TRANSFER_DOWNLOAD, persistent, FALSE);
1055 /* if we already have a connection don't send a push */
1056 if (persistent)
1057 return FALSE;
1059 /* now check for a persistent "pushed" connection, which would be stored
1060 * separately from a directly connected one */
1061 if (push_source_lookup_conn (gt->guid, gt->user_ip))
1062 return TRUE;
1064 /* send a push if the IP is local */
1065 if (gt_is_local_ip (gt->user_ip, gt->server_ip))
1066 return TRUE;
1068 /* don't send a push if we cannot receive incoming connections */
1069 if (gt_bind_is_firewalled())
1070 return FALSE;
1072 /* send a push if they set the firewalled bit */
1073 if (gt->firewalled)
1074 return TRUE;
1076 /* the last connection attempt failed, so try a push */
1077 if (gt->connect_failed)
1078 return TRUE;
1080 return FALSE;
1081 }
1083 static void handle_download (Chunk *chunk, GtTransfer *xfer, GtSource *gt)
1084 {
1085 /*
1086 * Send a push, or connect directly.
1087 */
1088 if (should_push (gt))
1089 {
1090 /* (possibly) retry a connection attempt next time */
1091 gt->connect_failed = FALSE;
1093 handle_push_download (chunk, xfer, gt);
1094 }
1095 else
1096 {
1097 gt_http_client_get (chunk, xfer);
1098 }
1099 }
1101 static BOOL download_is_queued (GtSource *gt)
1102 {
1103 /* back out if the request is still too early */
1104 if (time (NULL) < gt->retry_time)
1105 return TRUE;
1107 return FALSE;
1108 }
1110 int gnutella_download_start (Protocol *p, Transfer *transfer, Chunk *chunk,
1111 Source *source)
1112 {
1113 GtTransfer *xfer;
1114 GtSource *gt;
1115 off_t start;
1116 off_t stop;
1118 gt = source->udata;
1119 assert (gt != NULL);
1121 /* giftd should send us only deactivated Chunks */
1122 assert (chunk->udata == NULL);
1124 /* free the Source URL and update it with any format changes */
1125 replace_url (source, gt);
1127 /* thank you, pretender :) */
1128 start = chunk->start + chunk->transmit;
1129 stop = chunk->stop;
1131 if (!(xfer = gt_transfer_new (GT_TRANSFER_DOWNLOAD, source,
1132 gt->user_ip, gt->user_port, start, stop)))
1133 {
1134 GT->DBGFN (GT, "gt_transfer_new failed");
1135 return FALSE;
1136 }
1138 if (!set_request (xfer, chunk, source, gt))
1139 {
1140 gt_transfer_close (xfer, TRUE);
1141 return FALSE;
1142 }
1144 if (download_is_queued (gt))
1145 {
1146 detach_transfer (xfer, SOURCE_QUEUED_REMOTE, gt->status_txt);
1147 return TRUE;
1148 }
1150 handle_download (chunk, xfer, gt);
1152 return TRUE;
1153 }
1155 void gnutella_download_stop (Protocol *p, Transfer *transfer, Chunk *chunk,
1156 Source *source, BOOL complete)
1157 {
1158 gt_download_cancel (chunk, NULL);
1159 }
1161 int gnutella_upload_start (Protocol *p, Transfer *transfer, Chunk *chunk,
1162 Source *source, unsigned long avail)
1163 {
1164 return TRUE;
1165 }
1167 void gnutella_upload_stop (Protocol *p, Transfer *transfer, Chunk *chunk,
1168 Source *source)
1169 {
1170 gt_upload_cancel (chunk, NULL);
1171 }
1173 int gnutella_chunk_suspend (Protocol *p, Transfer *transfer, Chunk *chunk,
1174 Source *source)
1175 {
1176 return gt_chunk_suspend (chunk, transfer, NULL);
1177 }
1179 int gnutella_chunk_resume (Protocol *p, Transfer *transfer, Chunk *chunk,
1180 Source *source)
1181 {
1182 return gt_chunk_resume (chunk, transfer, NULL);
1183 }