/* 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 <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include "b_packet.h"
#include "btp_node.h"
#include "btp_debug.h"
#include "util.h"
/* **************************************** */
BPacket*
b_packet_new (BConn* conn, guint8 type, guint16 length)
{
BPacket* packet;
packet = (BPacket*) g_new0 (guchar, sizeof(BPacket) + length);
packet->protocol = B_PACKET_PROTOCOL;
packet->version = B_PACKET_VERSION;
packet->type = type;
packet->length = g_htons (length);
if (conn)
{
if (conn->group)
{
packet->seq = g_htons(conn->group->seq_num++);
packet->source_id = g_htonl(conn->group->source_id);
}
packet->id = conn->out_conn_id;
}
BTPP (1, "b_packet_new %p\n", packet);
return packet;
}
BPacket*
b_packet_clone (BPacket* pkt)
{
BPacket* new_pkt;
gint size;
g_return_val_if_fail (pkt, NULL);
size = sizeof(BPacket) + g_ntohs(pkt->length);
new_pkt = (BPacket*) g_malloc (size);
memcpy (new_pkt, pkt, size);
return new_pkt;
}
void
b_packet_delete (BPacket* packet)
{
BTPP (1, "b_packet_delete %p\n", packet);
if (packet)
g_free (packet);
}
/* ************************************************************ */
gboolean
b_packet_parse_addr (BPacket* pkt, BAddress** addrp)
{
guint len;
guint slen;
g_return_val_if_fail (pkt, TRUE);
g_return_val_if_fail (addrp, TRUE);
*addrp = NULL;
len = g_ntohs (pkt->length);
if (len == 0) return TRUE;
slen = strnlen (pkt->data, len);
if (slen != (len - 3)) return TRUE;
*addrp = g_new (BAddress, 1);
(*addrp)->hostname = g_strdup (pkt->data);
(*addrp)->port = ((unsigned char) pkt->data[slen+1] << 8) |
(unsigned char) pkt->data[slen+2];
return FALSE;
}
gboolean
b_packet_parse_addrs (BPacket* pkt, GSList** listp)
{
GSList* i;
guint len;
gchar* p;
guint slen;
g_return_val_if_fail (pkt, TRUE);
g_return_val_if_fail (listp, TRUE);
*listp = NULL;
len = g_ntohs (pkt->length);
p = pkt->data;
while (len > 0)
{
BAddress* addr;
slen = strnlen (p, len);
if (slen == len) goto error;
addr = g_new (BAddress, 1);
addr->hostname = g_strdup (p);
p += (slen + 1);
len -= (slen + 1);
if (len < 2) goto error;
addr->port = ((unsigned char) p[0] << 8) | (unsigned char) p[1];
p += 2;
len -= 2;
*listp = g_slist_prepend (*listp, addr);
}
*listp = g_slist_reverse (*listp);
return FALSE;
error:
for (i = *listp; i != NULL; i = i->next)
{
BAddress* addr = (BAddress*) i->data;
g_free (addr->hostname);
g_free (addr);
}
g_slist_free (*listp);
*listp = NULL;
return TRUE;
}
/* ************************************************************ */
/* Return bytes parsed, non-zero if error */
gint
b_packet_read (BConn* conn, const gchar* buffer, gint buflen, BPacket** packetp)
{
BPacket* pkt;
guint16 data_len;
BPacket* new_pkt;
gint new_pkt_len = 0;
guint16 copy_len;
g_return_val_if_fail (buffer, TRUE);
g_return_val_if_fail (buflen, TRUE);
g_return_val_if_fail (packetp, TRUE);
/* Initialize return values */
*packetp = NULL;
/* Read any remaining bytes for a previous variable length packet */
if (conn->cur_pkt)
{
pkt = conn->cur_pkt;
data_len = g_ntohs (pkt->length);
copy_len = MIN(data_len - conn->cur_read, buflen);
g_assert ((conn->cur_read + copy_len) <= data_len);
/* Copy the data */
memcpy (&pkt->data[conn->cur_read], buffer, copy_len);
/* Update total read */
conn->cur_read += copy_len;
/* Set packet if done */
if (conn->cur_read == data_len)
{
*packetp = conn->cur_pkt; /* pkt == conn->cur_pkt */
conn->cur_pkt = NULL;
conn->cur_read = 0;
}
/* Otherwise, there is more data to be received. We have read
all we can. */
else
g_assert (buflen == copy_len);
return copy_len;
}
/* Return if amount here smaller than a header */
if (buflen < sizeof(BPacket))
return 0;
/* Get the packet and length */
pkt = (BPacket*) buffer;
data_len = g_ntohs (pkt->length);
/* Make sure the protocol is right */
if (pkt->protocol != B_PACKET_PROTOCOL)
{
g_warning ("Received packet with bad protocol (%d)\n",
pkt->protocol);
return -1;
}
/* Make sure the version is right */
if (pkt->version != B_PACKET_VERSION)
{
g_warning ("Received packet with bad version (%d)\n",
pkt->version);
return -1;
}
/* Create a new packet */
new_pkt_len = sizeof(BPacket) + data_len;
new_pkt = (BPacket*) g_malloc (new_pkt_len);
/* Copy the header */
memcpy ((void*) new_pkt, buffer, sizeof(BPacket));
/* Copy the data */
copy_len = MIN(data_len, buflen - sizeof(BPacket));
memcpy (new_pkt->data, pkt->data, copy_len);
/* Save packet and break if we're expecting more data */
if (copy_len < data_len)
{
g_assert ((sizeof(BPacket) + copy_len) == buflen);
conn->cur_pkt = new_pkt;
conn->cur_read = copy_len;
return (sizeof(BPacket) + copy_len);
}
/* Otherwise, we have read the whole packet */
else
{
*packetp = new_pkt;
return new_pkt_len;
}
}
gint
b_packet_write (BPacket* packet, gchar** bufferp)
{
gint length;
g_return_val_if_fail (packet, 0);
g_return_val_if_fail (bufferp, 0);
length = B_PACKET_LENGTH(packet);
*bufferp = g_malloc (length);
memcpy (*bufferp, (void*) packet, length);
return length;
}
/* ************************************************************ */
BPacket*
b_packet_new_ok (BConn* conn, BPacketOkSubtype subtype)
{
BPacket* packet;
g_return_val_if_fail (conn != NULL, NULL);
packet = b_packet_new (conn, B_PACKET_TYPE_OK, 0);
packet->subtype = subtype;
return packet;
}
BPacket*
b_packet_new_error (BConn* conn, BPacketErrorSubtype subtype)
{
BPacket* packet;
g_return_val_if_fail (conn != NULL, NULL);
packet = b_packet_new (conn, B_PACKET_TYPE_ERROR, 0);
packet->subtype = subtype;
return packet;
}
BPacket*
b_packet_new_hello (BConn* bconn)
{
BPacket* pkt;
gchar* url_str;
guint length;
gint bytes_packed;
g_return_val_if_fail (bconn, NULL);
g_return_val_if_fail (bconn->conn, NULL);
g_return_val_if_fail (bconn->conn->hostname, NULL);
g_return_val_if_fail (bconn->group, NULL);
/* Send: "ssH"
URL
My hostname:port
*/
url_str = gnet_url_get_nice_string (bconn->group->url);
g_return_val_if_fail (url_str, NULL);
length = gnet_calcsize ("!ssH", url_str, bconn->group->peer->hostname);
g_return_val_if_fail (length > 0, NULL);
pkt = b_packet_new (bconn, B_PACKET_TYPE_HELLO, length);
bytes_packed = gnet_pack ("!ssH", pkt->data, length, url_str,
bconn->group->peer->hostname,
bconn->group->peer->port);
g_return_val_if_fail (length == bytes_packed, NULL);
pkt->id = bconn->in_conn_id; /* We send out id in first */
g_free (url_str);
return pkt;
}
gboolean /* Return FALSE if OK */
b_packet_parse_hello (BPacket* pkt, GURL** urlp,
gchar** hostnamep, gint* portp)
{
gchar* hostname;
gchar* portstr;
gchar* end;
g_return_val_if_fail (pkt, TRUE);
g_return_val_if_fail (urlp, TRUE);
g_return_val_if_fail (hostnamep, TRUE);
g_return_val_if_fail (portp, TRUE);
*urlp = NULL;
*hostnamep = NULL;
*portp = 0;
end = pkt->data + g_htons (pkt->length);
/* Get hostname */
hostname = pkt->data;
while (hostname < end && *hostname) ++hostname;
++hostname;
if (hostname >= end) return TRUE;
/* Get port string */
portstr = hostname;
while (portstr < end && *portstr) ++portstr;
++portstr;
if ((portstr + 2) != end) return TRUE;
/* Copy data */
*urlp = gnet_url_new (pkt->data);
if (!*urlp) return TRUE;
*hostnamep = g_strdup (hostname);
*portp = ((unsigned char) portstr[0] << 8) | (unsigned char) portstr[1];
return FALSE;
}
BPacket*
b_packet_new_ok_hello (BConn* conn)
{
BPacket* packet;
g_return_val_if_fail (conn != NULL, NULL);
packet = b_packet_new (conn, B_PACKET_TYPE_OK, sizeof(guint32));
packet->subtype = B_PACKET_OK_SUBTYPE_HELLO;
*(guint32*) packet->data = conn->in_conn_id;
return packet;
}
BPacket*
b_packet_new_bye (BConn* conn)
{
BPacket* packet;
g_return_val_if_fail (conn != NULL, NULL);
packet = b_packet_new (conn, B_PACKET_TYPE_BYE, 0);
return packet;
}
BPacket*
b_packet_new_ucast (BConn* conn, const void* buffer, guint16 length)
{
BPacket* packet;
packet = b_packet_new (conn, B_PACKET_TYPE_UCAST, length);
memcpy (packet->data, buffer, length);
return packet;
}
BPacket*
b_packet_new_mcast (BConn* conn, const void* buffer, guint16 length)
{
BPacket* packet;
packet = b_packet_new (conn, B_PACKET_TYPE_MCAST, length);
memcpy (packet->data, buffer, length);
return packet;
}
BPacket*
b_packet_new_info_request_ping (BConn* conn)
{
BPacket* packet;
packet = b_packet_new (conn, B_PACKET_TYPE_INFO_REQUEST, sizeof(struct timeval));
packet->subtype = B_PACKET_INFO_SUBTYPE_PING;
/* We will set the timeval just before we send it */
return packet;
}
BPacket*
b_packet_new_info_reply_ping (BConn* conn, BPacket* ping)
{
BPacket* packet;
guint length;
g_return_val_if_fail (conn != NULL, NULL);
g_return_val_if_fail (ping != NULL, NULL);
length = g_ntohs (ping->length);
packet = b_packet_new (conn, B_PACKET_TYPE_INFO_REPLY, length);
packet->subtype = B_PACKET_INFO_SUBTYPE_PING;
memcpy (packet->data, ping->data, length);
return packet;
}
BPacket*
b_packet_new_info_request_neighbors (BtpNode* node)
{
BPacket* packet;
g_return_val_if_fail (node, NULL);
g_return_val_if_fail (node->conn, NULL);
packet = b_packet_new (node->conn, B_PACKET_TYPE_INFO_REQUEST, 0);
packet->subtype = B_PACKET_INFO_SUBTYPE_NEIGHBORS;
return packet;
}
BPacket*
b_packet_new_info_reply_neighbors (BtpNode* node, BtpNode* parent, GSList* children)
{
GSList* i;
guint len = 0;
gchar* p;
BPacket* pkt;
g_return_val_if_fail (node, NULL);
g_return_val_if_fail (node->conn, NULL);
/* Calculate string length */
if (parent)
len += strlen (parent->hostname);
len += 1 + 2;
for (i = children; i != NULL; i = i->next)
{
BtpNode* child = (BtpNode*) i->data;
len += strlen (child->hostname) + 1;
len += 2;
}
/* Create packet */
pkt = b_packet_new (node->conn, B_PACKET_TYPE_INFO_REPLY, len);
pkt->subtype = B_PACKET_INFO_SUBTYPE_NEIGHBORS;
/* Copy in strings */
p = pkt->data;
if (parent)
{
strcpy (p, parent->hostname);
p += strlen (parent->hostname) + 1;
*p++ = ((guint) parent->port & 0xFF00) >> 8;
*p++ = parent->port & 0x00FF;
}
else
{
*p++ = '\0'; *p++ = '\0'; *p++ = '\0';
}
for (i = children; i != NULL; i = i->next)
{
BtpNode* child = (BtpNode*) i->data;
strcpy (p, child->hostname);
p += strlen (child->hostname) + 1;
*p++ = ((guint) child->port & 0xFF00) >> 8;
*p++ = child->port & 0x00FF;
}
return pkt;
}
gboolean
b_packet_parse_info_reply_neighbors (BPacket* pkt, BAddress** parent, GSList** children /* BAddress */)
{
g_return_val_if_fail (pkt, TRUE);
g_return_val_if_fail (parent, TRUE);
g_return_val_if_fail (children, TRUE);
/* Read addrs into children */
if (b_packet_parse_addrs (pkt, children))
return TRUE;
/* Pop off top and set to parent */
if (*children)
{
*parent = (BAddress*) (*children)->data;
*children = g_slist_remove (*children, *parent);
}
return FALSE;
}
BPacket*
b_packet_new_info_reply_node (BConn* conn, BtpNode* me)
{
gint len, rv;
BPacket* pkt;
/* Info:
uint32 delay
char* hostname
uint16 port
*/
len = gnet_calcsize ("!IsH", 0, me->hostname, me->port);
g_return_val_if_fail (len > 0, NULL);
pkt = b_packet_new (conn, B_PACKET_TYPE_INFO_REPLY, len);
pkt->subtype = B_PACKET_INFO_SUBTYPE_NODE;
rv = gnet_pack ("!IsH", pkt->data, len, 0, me->hostname, me->port);
g_return_val_if_fail (rv == len, NULL);
return pkt;
}
gboolean
b_packet_parse_info_reply_node (BPacket* pkt, guint32* delay, gchar** hostname, guint* port)
{
gint rv;
guint16 p;
guint len;
g_return_val_if_fail (delay && hostname && port, FALSE);
len = g_ntohs (pkt->length);
rv = gnet_unpack ("!IsH", pkt->data, len, delay, hostname, &p);
if (rv != len) return FALSE;
*port = p;
return TRUE;
}
BPacket*
b_packet_new_join (BtpNode* node)
{
BPacket* packet;
g_return_val_if_fail (node, NULL);
g_return_val_if_fail (node->conn, NULL);
packet = b_packet_new (node->conn, B_PACKET_TYPE_JOIN, 0);
return packet;
}
BPacket*
b_packet_new_switch (BtpNode* node, BtpNode* parent, BPacketSwitchSubtype sub)
{
guint slen;
BPacket* pkt;
g_return_val_if_fail (node, NULL);
g_return_val_if_fail (node->conn, NULL);
g_return_val_if_fail (parent, NULL);
slen = strlen (parent->hostname);
pkt = b_packet_new (node->conn, B_PACKET_TYPE_SWITCH, slen + 3);
pkt->subtype = sub;
memcpy (pkt->data, parent->hostname, slen);
pkt->data[slen + 0] = '\0';
pkt->data[slen + 1] = (parent->port & 0xFF00) >> 8;
pkt->data[slen + 2] = parent->port & 0x00FF;
return pkt;
}
gboolean
b_packet_parse_switch (BPacket* pkt, BAddress** addrp)
{
g_return_val_if_fail (pkt, TRUE);
if (pkt->subtype != B_PACKET_SWITCH_SUBTYPE_SIBLING &&
pkt->subtype != B_PACKET_SWITCH_SUBTYPE_GPARENT)
{
g_warning ("Bad SWITCH packet subtype\n");
return TRUE;
}
return b_packet_parse_addr (pkt, addrp);
}
BPacket*
b_packet_new_leave (BtpNode* node)
{
BPacket* packet;
g_return_val_if_fail (node, NULL);
g_return_val_if_fail (node->conn, NULL);
packet = b_packet_new (node->conn, B_PACKET_TYPE_LEAVE, 0);
return packet;
}
/* ************************************************************ */
static gchar* type_to_string[] =
{
"OK",
"ERROR",
"HELLO",
"BYE",
"UNICAST",
"MULTICAST",
"INFO_REQUEST",
"INFO_REPLY",
"JOIN",
"LEAVE",
"SWITCH",
"SHORTCUT_ADD",
"SHORTCUT_REMOVE"
};
gchar*
b_packet_type_to_string (gint type)
{
if (type < 0 || type > B_PACKET_TYPE_LAST)
return "<null>";
return type_to_string[type];
}
void
b_address_delete (BAddress* addr)
{
if (addr)
{
g_free (addr->hostname);
g_free (addr);
}
}
void
b_addresses_delete (GSList* list)
{
if (list)
{
GSList* i;
for (i = list; i != NULL; i = i->next)
b_address_delete ((BAddress*) i->data);
g_slist_free (list);
}
}
syntax highlighted by Code2HTML, v. 0.9.1