/* 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 <stdlib.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1