/* BTP library - Banana Tree Protocol * Copyright (C) 1999-2001 The Regents of the University of Michigan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "util.h" #include "btp.h" #include "btp_node.h" #include "btp_proto.h" #include "b_packet.h" #include "btp_debug.h" static void mcast_read_cb (BtpTree* tree, BtpNode* from_node, guint8* buffer, guint length, gpointer user_data); static void ucast_read_cb (BtpTree* tree, BtpNode* from_node, guint8* buffer, guint length, gpointer user_data); static void error_cb (BtpTree* tree, gpointer user_data); static guint btp_max_degree = BTP_MAX_DEGREE; static gboolean btp_follow_nodes = TRUE; static gboolean btp_use_shortcuts = FALSE; /* ************************************************************ */ /** Create a new BTP tree. Last checked: 2001-5-7 DAH */ Btp* btp_create (BPeer* peer, const gchar* name) { GURL* url; Btp* btp = NULL; BtpTree* tree = NULL; g_return_val_if_fail (peer, NULL); g_return_val_if_fail (name, NULL); BTPP (1, "btp_create %s\n", name); /* ******************** */ /* Check if tree already exists */ if (btp_has (peer, name)) return NULL; /* Create URL */ if (*name == '/') url = gnet_url_new_fields ("btp", peer->hostname, peer->port, name); else { url = gnet_url_new_fields ("btp", peer->hostname, peer->port, NULL); url->resource = g_strconcat ("/", name, NULL); } /* Create tree */ tree = btp_tree_new (peer, url); /* Set initial sequence number */ tree->group.seq_num = rand() % G_MAXUINT16; tree->group.source_id = rand() % G_MAXUINT32; /* Set default policies */ tree->max_degree = btp_max_degree; tree->follow_nodes = btp_follow_nodes; tree->use_shortcuts = btp_use_shortcuts; /* Add me to tree */ tree->me = btp_node_new (tree, peer->hostname, peer->port, NULL); /* Make me root */ tree->root = tree->me; tree->parent = NULL; /* Add peer handler */ b_peer_add_handler (peer, url->resource, btp_peer_handler, tree); /* Create BTP */ btp = g_new0 (Btp, 1); btp->tree = tree; /* Set callbacks */ tree->mcast_read_func = mcast_read_cb; tree->ucast_read_func = ucast_read_cb; tree->error_func = error_cb; tree->mcast_read_user_data = btp; tree->ucast_read_user_data = btp; tree->error_user_data = btp; /* Initialize */ btp_proto_init (tree); return btp; } /** Join an existing BtpTree. Last checked: 2001-5-7 DAH */ Btp* btp_join (BPeer* peer, const GURL* url) { Btp* btp; BtpTree* tree; GURL* gurl; g_return_val_if_fail (peer, NULL); g_return_val_if_fail (url, NULL); /* ******************** */ /* Check if we have this tree. b_peer would get confused because it doesn't know if an incoming connection is going to us, or to the root. TODO: Allow this. */ if (btp_has (peer, url->resource?url->resource:"/")) return NULL; gurl = gnet_url_clone (url); /* do not delete */ if (gurl->port == 0) gurl->port = BTP_PORT; /* Create tree */ tree = btp_tree_new (peer, gurl); /* Set initial sequence number */ tree->group.seq_num = rand() % G_MAXUINT16; tree->group.source_id = rand() % G_MAXUINT32; /* Set default policies */ tree->max_degree = btp_max_degree; tree->follow_nodes = btp_follow_nodes; tree->use_shortcuts = btp_use_shortcuts; /* Create me */ tree->me = btp_node_new (tree, peer->hostname, peer->port, NULL); /* Create root and connect */ tree->root = btp_node_new (tree, gurl->hostname, gurl->port, NULL); tree->parent = tree->root; b_conn_connect (tree->root->conn); /* Add peer handler */ b_peer_add_handler (peer, url->resource?url->resource:"/", btp_peer_handler, tree); /* Create BTP */ btp = g_new0 (Btp, 1); btp->tree = tree; /* Set callbacks */ tree->mcast_read_func = mcast_read_cb; tree->ucast_read_func = ucast_read_cb; tree->error_func = error_cb; tree->mcast_read_user_data = btp; tree->ucast_read_user_data = btp; tree->error_user_data = btp; /* Initialize */ btp_proto_init (tree); return btp; } /** */ Btp* btp_join_passive (BPeer* peer, const GURL* url) { Btp* btp; g_return_val_if_fail (peer, NULL); g_return_val_if_fail (url, NULL); /* ******************** */ btp = btp_join (peer, url); if (!btp) return NULL; btp->tree->passive = TRUE; /* We have no parent in passive mode */ btp->tree->parent = NULL; return btp; } void btp_leave (Btp* btp) { BGroup* group; g_return_if_fail (btp); group = &btp->tree->group; b_peer_remove_handler (group->peer, group->url->resource); gnet_url_delete (group->url); btp_tree_delete (btp->tree); memset (btp, 0, sizeof(*btp)); g_free (btp); } gboolean btp_has (BPeer* peer, const gchar* name) { gchar* tname; gboolean rv; g_return_val_if_fail (peer, FALSE); g_return_val_if_fail (name, FALSE); /* Clean up name so it starts with / */ if (*name == '/') tname = g_strdup (name); else tname = g_strconcat ("/", name, NULL); rv = b_peer_has_handler (peer, tname); g_free (tname); return rv; } guint btp_get_max_degree (Btp* btp) { if (btp) return btp->tree->max_degree; else return btp_max_degree; } void btp_set_max_degree (Btp* btp, guint max_degree) { if (btp) btp->tree->max_degree = max_degree; else btp_max_degree = max_degree; } gboolean btp_get_follow_nodes (Btp* btp) { if (btp) return btp->tree->follow_nodes; else return btp_follow_nodes; } void btp_set_follow_nodes (Btp* btp, gboolean follow_nodes) { if (btp) btp->tree->follow_nodes = follow_nodes; else btp_follow_nodes = follow_nodes; } gboolean btp_get_use_shortcuts (Btp* btp) { if (btp) return btp->tree->use_shortcuts; else return btp_use_shortcuts; } void btp_set_use_shortcuts (Btp* btp, gboolean use_shortcuts) { if (btp) btp->tree->use_shortcuts = use_shortcuts; else btp_use_shortcuts = use_shortcuts; } GURL* btp_get_url (Btp* btp) { g_return_val_if_fail (btp, NULL); return btp->tree->group.url; } gboolean btp_is_up (const Btp* btp) { if (btp && btp->tree) { if (btp_tree_is_root (btp->tree->me)) return TRUE; if (btp->tree->parent && btp->tree->parent->conn && b_conn_is_connected (btp->tree->parent->conn)) return TRUE; } return FALSE; } void btp_print (FILE* file, BPeer* peer) { GSList* trees; GSList* i; g_return_if_fail (file); g_return_if_fail (peer); trees = b_peer_get_handler_data (peer); fprintf (file, "BtpPeer\n"); for (i = trees; i != NULL; i = i->next) btp_tree_print (file, (BtpTree*) i->data); g_slist_free (trees); } void btp_send (Btp* btp, const guint8* buffer, guint16 length) { g_return_if_fail (btp); btp_tree_send_mcast (btp->tree, buffer, length); } /* **************************************** */ /* Callbacks */ static void mcast_read_cb (BtpTree* tree, BtpNode* from_node, guint8* buffer, guint length, gpointer user_data) { Btp* btp = (Btp*) user_data; /* Pass packet up */ if (btp->packet_func) (btp->packet_func)(btp, buffer, length, btp->packet_user_data); } static void ucast_read_cb (BtpTree* tree, BtpNode* from_node, guint8* buffer, guint length, gpointer user_data) { Btp* btp = (Btp*) user_data; /* Pass packet up */ if (btp->packet_func) (btp->packet_func)(btp, buffer, length, btp->packet_user_data); } static void error_cb (BtpTree* tree, gpointer user_data) { Btp* btp = (Btp*) user_data; /* Pass error up */ if (btp->error_func) (btp->error_func)(btp, btp->error_user_data); }