/* 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 #include "b_conn.h" #include "b_packet.h" #include "btp_debug.h" #include "util.h" guint b_conn_num_conns = 0; void (*b_conn_num_conns_zero)(void) = NULL; static void b_conn_reset (BConn* conn); /* ************************************************************ */ BConn* b_conn_new (gchar* hostname, gint port, BGroup* group, BConnFuncs* funcs, gpointer user_data) { BConn* conn; g_return_val_if_fail (hostname, NULL); g_return_val_if_fail (port != 0, NULL); g_return_val_if_fail (group, NULL); BTPP (1, "b_conn_new\n"); conn = g_new0 (BConn, 1); conn->conn = gnet_conn_new (hostname, port, NULL, 0); conn->group = group; conn->funcs = funcs; conn->user_data = user_data; conn->status = B_CONN_STATUS_UNCONNECTED; ++b_conn_num_conns; return conn; } BConn* b_conn_new_accepted (GConn* gconn) { BConn* conn; conn = g_new0 (BConn, 1); conn->conn = gconn; conn->status = B_CONN_STATUS_ACCEPTED; ++b_conn_num_conns; return conn; } void b_conn_delete (BConn* conn) { g_return_if_fail (conn != NULL); BTPP (1, "b_conn_delete\n"); /* If we're in an upcall, don't delete yet */ if (conn->in_upcall) { conn->should_destroy = 1; return; } b_conn_reset (conn); gnet_conn_delete (conn->conn, FALSE); memset (conn, 0, sizeof(*conn)); g_free (conn); /* Call callback */ if (b_conn_num_conns) { --b_conn_num_conns; if (!b_conn_num_conns && b_conn_num_conns_zero) (b_conn_num_conns_zero)(); } else g_warning ("b_conn_num_conns invalid\n"); } void b_conn_reset (BConn* conn) { GSList* i; g_return_if_fail (conn); BTPP (1, "b_conn_reset\n"); conn->status = B_CONN_STATUS_UNCONNECTED; /* Stop pinging it */ b_conn_ping_stop (conn); /* keep the distance estimate */ /* Reset BYE timer */ rand_timer_cancel (&conn->bye_to_timer); /* Remove all IO handlers */ b_conn_ignore_all (conn); BTPP (1, "b_conn_reset: disconnect %p\n", conn->conn); /* Reset connection */ gnet_conn_disconnect (conn->conn, TRUE); BTPP (1, "b_conn_reset: disconnect done\n"); /* Remove all handlers */ b_conn_handler_remove_all (conn); /* Reset packet management stuff */ conn->in_conn_id = 0; conn->out_conn_id = 0; BTPP (1, "b_conn_reset: delete packets\n"); for (i = conn->pkt_queue; i != NULL; i = i->next) b_packet_delete ((BPacket*) i->data); g_slist_free (conn->pkt_queue); conn->pkt_queue = NULL; conn->pkt_queue_size = 0; conn->buffer_offset = 0; conn->cur_read = 0; b_packet_delete (conn->cur_pkt); conn->cur_pkt = NULL; BTPP (1, "b_conn_reset: done\n"); } void b_conn_print (FILE* file, BConn* conn) { if (conn) { gchar* status = "none"; switch (conn->status) { case B_CONN_STATUS_UNCONNECTED: status = "unconnected"; break; case B_CONN_STATUS_CLOSING: status = "closing"; break; case B_CONN_STATUS_CONNECTED: status = "connected"; break; case B_CONN_STATUS_CONNECTING: status = "connecting"; break; case B_CONN_STATUS_ACCEPTED: status = "accepted"; break; } fprintf (file, "%s:%d [status = %s, distance = %d, " "pktq length = %d, pktq size = %d, handlers = ", conn->conn->hostname, conn->conn->port, status, conn->distance, g_slist_length (conn->pkt_queue), conn->pkt_queue_size); b_conn_handler_print (file, conn); fprintf (file, "]\n"); } else { fprintf (file, ""); } } /* **************************************** */ void b_conn_func_connect (BConn* conn) { g_return_if_fail (conn); BTPP (1, "b_conn_func_connect\n"); if (conn->funcs && conn->funcs->connect) conn->funcs->connect (conn, conn->user_data); } void b_conn_func_ping (BConn* conn) { g_return_if_fail (conn); BTPP (1, "b_conn_func_ping\n"); if (conn->funcs && conn->funcs->ping) conn->funcs->ping (conn, conn->user_data); } void b_conn_func_writeable (BConn* conn) { g_return_if_fail (conn); BTPP (1, "b_conn_func_writeable\n"); if (conn->funcs && conn->funcs->writeable) conn->funcs->writeable (conn, conn->user_data); } void b_conn_func_close (BConn* conn) { g_return_if_fail (conn); BTPP (1, "b_conn_func_close\n"); b_conn_reset (conn); if (conn->funcs && conn->funcs->close) conn->funcs->close (conn, conn->user_data); } void b_conn_func_fail (BConn* conn) { g_return_if_fail (conn); BTPP (1, "b_conn_func_fail\n"); b_conn_reset (conn); if (conn->funcs && conn->funcs->fail) conn->funcs->fail (conn, conn->user_data); } /* **************************************** */ gboolean b_conn_has_packet (BConn* conn, guint8 type) { GSList* i; g_return_val_if_fail (conn != NULL, FALSE); for (i = conn->pkt_queue; i != NULL; i = i->next) { BPacket* packet = (BPacket*) i->data; if (packet->type == type) return TRUE; } return FALSE; } gboolean b_conn_has_packet_by_subtype (BConn* conn, guint8 type, guint8 subtype) { GSList* i; g_return_val_if_fail (conn != NULL, FALSE); for (i = conn->pkt_queue; i != NULL; i = i->next) { BPacket* packet = (BPacket*) i->data; if (packet->type == type && packet->subtype == subtype) return TRUE; } return FALSE; } /* **************************************** */ void b_conn_send_packet (BConn* bconn, BPacket* packet) { g_return_if_fail (bconn); g_return_if_fail (packet); BTPP (1, "b_conn_send_packet: proto = %d, version = %d, type = %d, %d, length = %d, id = %d\n", packet->protocol, packet->version, packet->type, packet->subtype, g_ntohs(packet->length), packet->id); BTPP (3, "SEND %s (%d)\n", b_packet_type_to_string(packet->type), packet->subtype); /* Connect if unconnected */ if (b_conn_is_unconnected(bconn)) { b_conn_connect (bconn); } /* Make sure we're watching for a write */ else if (gnet_conn_is_connected(bconn->conn)) { b_conn_watch_write (bconn); } /* If the packet is a HELLO, put it at the front of the packet list */ if (packet->type == B_PACKET_TYPE_HELLO) { /* Remove any HELLOs that are in the queue now */ b_conn_remove_packets (bconn, B_PACKET_TYPE_HELLO); /* Put at the front of the queue */ b_conn_prepend_packet (bconn, packet); } /* Put any reply immediately after the last reply or at the front, which ever is first */ else if (packet->type == B_PACKET_TYPE_OK || packet->type == B_PACKET_TYPE_ERROR) { GSList* i; GSList* last_reply = NULL; for (i = bconn->pkt_queue; i != NULL; i = i->next) { gint t = ((BPacket*) i->data)->type; if (t == B_PACKET_TYPE_OK || t == B_PACKET_TYPE_ERROR) last_reply = i; } if (last_reply) last_reply = g_slist_insert (last_reply, packet, 1); else bconn->pkt_queue = g_slist_prepend (bconn->pkt_queue, packet); } /* If the packet is MCAST data, make sure we have enough room. */ else if (packet->type == B_PACKET_TYPE_MCAST) { if ((bconn->pkt_queue_size + g_ntohs(packet->length)) < B_CONN_MAX_MCAST_SIZE) { b_conn_append_packet (bconn, packet); bconn->pkt_queue_size += g_ntohs(packet->length); } else { BTPP (15, "Dropping MCAST packet due to space\n"); } } /* Otherwise, its just a regular packet, so add it to the back of the list */ else b_conn_append_packet (bconn, packet); } void b_conn_prepend_packet (BConn* conn, BPacket* packet) { g_return_if_fail (conn); g_return_if_fail (packet); conn->pkt_queue = g_slist_prepend (conn->pkt_queue, packet); } void b_conn_append_packet (BConn* conn, BPacket* packet) { g_return_if_fail (conn); g_return_if_fail (packet); conn->pkt_queue = g_slist_append (conn->pkt_queue, packet); } /* **************************************** */ BPacket* b_conn_pop_packet (BConn* conn) { BPacket* packet = NULL; BPacket* data_packet = NULL; GSList* i; g_return_val_if_fail (conn != NULL, NULL); BTPP (1, "b_conn_pop_packet\n"); if (conn->pkt_queue == NULL) return NULL; for (i = conn->pkt_queue; i != NULL; i = i->next) { BPacket* pkt = (BPacket*) i->data; BTPP (1, "b_conn_pop_packet: QUEUE type = %d, %d, length = %d\n", pkt->type, pkt->subtype, g_ntohs(pkt->length)); } /* Search for a control packet (other than BYE). Also keep track of the first data packet we see. */ for (i = conn->pkt_queue; i != NULL; i = i->next) { BPacket* p = (BPacket*) i->data; if (p->type == B_PACKET_TYPE_MCAST || p->type == B_PACKET_TYPE_UCAST) { if (data_packet == NULL) data_packet = p; } else if (p->type != B_PACKET_TYPE_BYE) { packet = p; break; } /* otherwise it's a BYE, ignore it for now. This effectively pushes the BYE to the back of the queue. */ } /* If we didn't find a control packet, use a data packet. If we didn't find a data packet, use the top packet, which should be a BYE. */ if (packet == NULL) { if (data_packet != NULL) packet = data_packet; else { packet = (BPacket*) conn->pkt_queue->data; g_return_val_if_fail (packet->type == B_PACKET_TYPE_BYE, NULL); } } g_return_val_if_fail (packet != NULL, NULL); /* If we aren't connected and this is a non-connect packet, return NULL. We don't want to send anything special yet. */ if (conn->status == B_CONN_STATUS_CONNECTING && packet->type != B_PACKET_TYPE_HELLO) return NULL; /* Remove the packet from the list */ conn->pkt_queue = g_slist_remove (conn->pkt_queue, packet); BTPP (1, "b_conn_pop_packet: remove type = %d, %d, lenght = %d\n", packet->type, packet->subtype, g_ntohs(packet->length)); /* If it's a ping, set the time now. (If we set it earlier, it might sit around in our buffers before we actually sent it, which could cause a bad distance estimate.) */ if (packet->type == B_PACKET_TYPE_INFO_REQUEST && packet->subtype == B_PACKET_INFO_SUBTYPE_PING) { BTPP (1, "b_conn_pop_packet: update ping\n"); gettimeofday((struct timeval*) packet->data, NULL); } /* Otherwise, if it's a MCAST packet, deduct it's length from pkt_queue_size. */ else if (packet->type == B_PACKET_TYPE_MCAST) { conn->pkt_queue_size -= g_ntohs(packet->length); } return packet; } void b_conn_remove_packets (BConn* conn, guint8 type) { GSList* i; BTPP (1, "b_conn_remove_packets\n"); for (i = conn->pkt_queue; i != NULL; ) { GSList* old_i = i; BPacket* packet = (BPacket*) i->data; i = i->next; if (packet->type == type) { if (packet->type == B_PACKET_TYPE_MCAST) conn->pkt_queue_size -= g_ntohs (packet->length); b_packet_delete (packet); conn->pkt_queue = g_slist_remove_link (conn->pkt_queue, old_i); } } } void b_conn_remove_packets_by_subtype (BConn* conn, guint8 type, guint8 subtype) { GSList* i; BTPP (1, "b_conn_remove_packets_by_subtype\n"); for (i = conn->pkt_queue; i != NULL; ) { GSList* old_i = i; BPacket* packet = (BPacket*) i->data; i = i->next; if (packet->type == type && packet->subtype == subtype) { if (packet->type == B_PACKET_TYPE_MCAST) conn->pkt_queue_size -= g_ntohs (packet->length); b_packet_delete (packet); conn->pkt_queue = g_slist_remove_link (conn->pkt_queue, old_i); } } }