/* 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_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 ""; 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); } }