/* 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 "b_conn_connect.h" #include "b_packet.h" #include "btp_debug.h" static gboolean connect_gconn_func (GConn* conn, GConnStatus status, gchar* buffer, gint length, gpointer user_data); static void receive_ok_connect (BConn* conn, BPacket* packet, gpointer user_data); static void receive_error_connect (BConn* conn, BPacket* packet, gpointer user_data); static void receive_ok_timeout (BConn* conn, gint type, gint subtype, gpointer user_data); static void receive_bye (BConn* conn, BPacket* packet, gpointer user_data); static gboolean bye_timeout_cb (gpointer data); /* ************************************************************ */ /* Connect to a host. We assume the conn is unconnected when this is called. Two hosts are connected if they have exchanged a HELLO-OK pair of packets. Last checked: 2001-5-1 DAH */ void b_conn_connect (BConn* bconn) { g_return_if_fail (bconn); g_return_if_fail (bconn->conn); BTPP (1, "b_conn_connect %s:%d\n", bconn->conn->hostname, bconn->conn->port); /* Ignore if we aren't unconnected (ie, we're connected or connecting) */ if (!b_conn_is_unconnected(bconn)) return; /* Double check: we shouldn't have a low-level connection */ g_return_if_fail (!gnet_conn_is_connected (bconn->conn)); /* Dub is connecting */ bconn->status = B_CONN_STATUS_CONNECTING; /* Set callbacks for low level connection */ bconn->conn->func = connect_gconn_func; bconn->conn->user_data = bconn; BTPP (11, "CONNECT START %s:%d\n", bconn->conn->hostname, bconn->conn->port); /* Initiate low-level connection */ gnet_conn_connect (bconn->conn, BCONNECT_CONNECT_TIME); } static gboolean connect_gconn_func (GConn* conn, GConnStatus status, gchar* buffer, gint length, gpointer user_data) { BConn* bconn = (BConn*) user_data; g_return_val_if_fail (bconn, FALSE); /* Don't need to call this again */ conn->func = NULL; conn->user_data = NULL; switch (status) { case GNET_CONN_STATUS_CONNECT: { BPacket* packet; /* Set up watches */ b_conn_watch_read (bconn); b_conn_watch_error (bconn); /* We will add the write watch when we call send_packet */ /* Assign the conn an ID. For now we just use a random number, since the TCP connection is enough to identify it after fully connecting. */ bconn->in_conn_id = rand(); BTPP (0, "connect_gconn_func: in_conn_id = %d\n", bconn->in_conn_id); BTPP (11, "CONNECT ACCEPTED %s:%d\n", bconn->conn->hostname, bconn->conn->port); /* Send a HELLO packet */ packet = b_packet_new_hello (bconn); b_conn_send_packet (bconn, packet); /* Handle an OK packet */ b_conn_handler_add_full (bconn, B_PACKET_TYPE_OK, B_PACKET_OK_SUBTYPE_HELLO, receive_ok_connect, receive_ok_timeout, BCONNECT_RECEIVE_OK_TIME, NULL); /* Handle an ERROR packet (don't need a timeout - OK timeout is enough) */ b_conn_handler_add (bconn, B_PACKET_TYPE_ERROR, B_PACKET_ERROR_SUBTYPE_HELLO, receive_error_connect, NULL); break; } case GNET_CONN_STATUS_ERROR: case GNET_CONN_STATUS_TIMEOUT: { BTPP (11, "CONNECT ERROR %s:%d\n", bconn->conn->hostname, bconn->conn->port); /* Fail the node */ b_conn_func_fail (bconn); break; } default: { g_assert_not_reached(); break; } } return FALSE; } static void receive_ok_connect (BConn* conn, BPacket* packet, gpointer user_data) { GSList* i; BTPP (1, "receive_ok_connect\n"); /* Stop handling OK and ERROR */ b_conn_handler_remove (conn, B_PACKET_TYPE_OK, B_PACKET_OK_SUBTYPE_HELLO, NULL); b_conn_handler_remove (conn, B_PACKET_TYPE_ERROR, B_PACKET_ERROR_SUBTYPE_HELLO, NULL); /* The packet should have an ID in it */ if (g_ntohs(packet->length) != sizeof(packet->id)) { g_warning ("Received HELLO-OK without ID\n"); b_conn_func_fail (conn); return; } /* Save the ID of the conn */ conn->out_conn_id = *(guint32*) packet->data; /* Fix the ID of any packets in our packet queue */ for (i = conn->pkt_queue; i != NULL; i = i->next) ((BPacket*) i->data)->id = conn->out_conn_id; /* Dub it connected */ conn->status = B_CONN_STATUS_CONNECTED; /* Start sending pings */ b_conn_ping_start (conn); b_conn_ping_handle (conn); b_conn_bye_handle (conn); /* Add a write watch if we have packets to send */ if (conn->pkt_queue) b_conn_watch_write (conn); BTPP (11, "CONNECT COMPLETE %s:%d\n", conn->conn->hostname, conn->conn->port); /* Call up */ b_conn_func_connect (conn); } static void receive_error_connect (BConn* conn, BPacket* packet, gpointer user_data) { BTPP (1, "receive_error_connect\n"); BTPP (11, "CONNECT ERROR2 %s:%d\n", conn->conn->hostname, conn->conn->port); b_conn_func_fail (conn); } static void receive_ok_timeout (BConn* conn, gint type, gint subtype, gpointer user_data) { BTPP (1, "receive_ok_timeout\n"); BTPP (11, "CONNECT TIMEOUT %s:%d\n", conn->conn->hostname, conn->conn->port); b_conn_func_fail (conn); } /* ************************************************************ */ /** Close a connection. If the connection is already closed, the connection is closed immediately. Otherwise, we send a BYE message. Last checked: 2001-5-1 DAH */ void b_conn_close (BConn* conn) { BPacket* packet; g_return_if_fail (conn); BTPP (1, "b_conn_close\n"); /* Close immediately if we aren't even connected. This will reset the connection and do the upcall. */ if (!b_conn_is_connected(conn)) { b_conn_func_close (conn); return; } BTPP (11, "CONNECT CLOSE %s:%d\n", conn->conn->hostname, conn->conn->port); /* Stop pinging the host */ b_conn_ping_stop (conn); /* Ignore any packets we receive */ b_conn_ignore_read (conn); /* Send a BYE */ packet = b_packet_new_bye (conn); b_conn_send_packet (conn, packet); /* Set a timer on sending the BYE. The timer will be canceled with the BYE is sent. If it is not cancelled, we will close the connection by force. */ g_return_if_fail (!conn->bye_to_timer); conn->bye_to_timer = g_timeout_add (BCONNECT_BYE_TIME, bye_timeout_cb, conn); /* Set the status to closing */ conn->status = B_CONN_STATUS_CLOSING; } /* ************************************************************ */ void b_conn_bye_handle (BConn* conn) { g_return_if_fail (conn); g_return_if_fail (b_conn_is_connected (conn)); g_return_if_fail (!b_conn_is_closing (conn)); /* Start handling BYE */ b_conn_handler_add (conn, B_PACKET_TYPE_BYE, 0, receive_bye, NULL); } void b_conn_bye_unhandle (BConn* conn) { g_return_if_fail (conn); /* Stop handling BYE */ b_conn_handler_remove (conn, B_PACKET_TYPE_BYE, 0, NULL); } static void receive_bye (BConn* conn, BPacket* packet, gpointer user_data) { g_return_if_fail (conn); g_return_if_fail (packet); g_return_if_fail (packet->type == B_PACKET_TYPE_BYE); BTPP (1, "receive_bye\n"); b_conn_func_close (conn); } static gboolean bye_timeout_cb (gpointer data) { BConn* conn = (BConn*) conn; b_conn_func_fail (conn); return FALSE; }