paulo@0: /* paulo@0: * $Id: gt_message.c,v 1.6 2004/01/07 07:24:43 hipnod Exp $ paulo@0: * paulo@0: * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net) paulo@0: * paulo@0: * This program is free software; you can redistribute it and/or modify it paulo@0: * under the terms of the GNU General Public License as published by the paulo@0: * Free Software Foundation; either version 2, or (at your option) any paulo@0: * later version. paulo@0: * paulo@0: * This program is distributed in the hope that it will be useful, but paulo@0: * WITHOUT ANY WARRANTY; without even the implied warranty of paulo@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU paulo@0: * General Public License for more details. paulo@0: */ paulo@0: paulo@0: #include "gt_gnutella.h" paulo@0: #include "msg_handler.h" paulo@0: paulo@0: #include "gt_netorg.h" paulo@0: #include "gt_connect.h" paulo@0: paulo@0: #include "gt_utils.h" paulo@0: paulo@0: #include "io/rx_stack.h" /* gt_rx_stack_new */ paulo@0: #include "io/tx_stack.h" /* gt_tx_stack_new */ paulo@0: paulo@0: #include "gt_message.h" paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: extern void gt_vmsg_send_supported (GtNode *node); /* vendor.c */ paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: extern GT_MSG_HANDLER(gt_msg_ping); paulo@0: extern GT_MSG_HANDLER(gt_msg_ping_reply); paulo@0: extern GT_MSG_HANDLER(gt_msg_bye); paulo@0: extern GT_MSG_HANDLER(gt_msg_push); paulo@0: extern GT_MSG_HANDLER(gt_msg_query_route); paulo@0: extern GT_MSG_HANDLER(gt_msg_query); paulo@0: extern GT_MSG_HANDLER(gt_msg_query_reply); paulo@0: extern GT_MSG_HANDLER(gt_msg_vendor); paulo@0: paulo@0: static struct msg_handler paulo@0: { paulo@0: uint8_t command; paulo@0: GtMessageHandler func; paulo@0: } paulo@0: msg_handler_table[] = paulo@0: { paulo@0: /* table listed not in numerical order, but by frequency of messages */ paulo@0: { GT_MSG_QUERY, gt_msg_query }, paulo@0: { GT_MSG_QUERY_REPLY, gt_msg_query_reply }, paulo@0: { GT_MSG_PING_REPLY, gt_msg_ping_reply }, paulo@0: { GT_MSG_PING, gt_msg_ping }, paulo@0: { GT_MSG_PUSH, gt_msg_push }, paulo@0: { GT_MSG_QUERY_ROUTE, gt_msg_query_route }, paulo@0: { GT_MSG_VENDOR, gt_msg_vendor }, paulo@0: { GT_MSG_VENDOR_STD, gt_msg_vendor }, /* same as non-standard */ paulo@0: { GT_MSG_BYE, gt_msg_bye }, paulo@0: { 0x00, NULL } paulo@0: }; paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static BOOL handle_message (TCPC *c, GtPacket *packet) paulo@0: { paulo@0: struct msg_handler *handler; paulo@0: uint8_t command; paulo@0: paulo@0: if (!packet) paulo@0: return FALSE; paulo@0: paulo@0: command = gt_packet_command (packet); paulo@0: paulo@0: /* locate the handler */ paulo@0: for (handler = msg_handler_table; handler->func; handler++) paulo@0: { paulo@0: if (command == handler->command) paulo@0: { paulo@0: handler->func (GT_NODE(c), c, packet); paulo@0: return TRUE; paulo@0: } paulo@0: } paulo@0: paulo@0: GIFT_ERROR (("[%s] found no handler for cmd %hx, payload %hx", paulo@0: net_ip_str (GT_NODE(c)->ip), command, paulo@0: gt_packet_payload_len (packet))); paulo@0: paulo@0: return FALSE; paulo@0: } paulo@0: paulo@0: static void cleanup_node_rx (GtNode *node) paulo@0: { paulo@0: TCPC *c = GT_CONN(node); paulo@0: paulo@0: assert (GT_NODE(c) == node); paulo@0: gt_node_disconnect (c); paulo@0: } paulo@0: paulo@0: /* TODO: make this the same type as cleanup_node_rx */ paulo@0: static void cleanup_node_tx (GtTxStack *stack, GtNode *node) paulo@0: { paulo@0: TCPC *c = GT_CONN(node); paulo@0: paulo@0: assert (GT_NODE(c) == node); paulo@0: gt_node_disconnect (c); paulo@0: } paulo@0: paulo@0: static void recv_packet (GtNode *node, GtPacket *packet) paulo@0: { paulo@0: assert (packet != NULL); paulo@0: paulo@0: gt_packet_log (packet, GT_CONN(node), FALSE); paulo@0: (void)handle_message (node->c, packet); paulo@0: } paulo@0: paulo@0: /* Find out what our IP is */ paulo@0: static in_addr_t get_self_ip (TCPC *c) paulo@0: { paulo@0: in_addr_t our_ip; paulo@0: char *ip_str; paulo@0: paulo@0: if ((ip_str = dataset_lookupstr (GT_NODE(c)->hdr, "remote-ip"))) paulo@0: { paulo@0: /* paulo@0: * Since we may be firewalled, we may not know what our ip is. So set paulo@0: * the ip from what the other node thinks it is. paulo@0: * paulo@0: * Doing this allows you to setup port forwarding on a firewall paulo@0: * and accept incoming connections. paulo@0: */ paulo@0: our_ip = net_ip (ip_str); paulo@0: } paulo@0: else paulo@0: { paulo@0: struct sockaddr_in saddr; paulo@0: int len = sizeof (saddr); paulo@0: paulo@0: if (getsockname (c->fd, (struct sockaddr *)&saddr, &len) == 0) paulo@0: our_ip = saddr.sin_addr.s_addr; paulo@0: else paulo@0: our_ip = net_ip ("127.0.0.1"); paulo@0: } paulo@0: paulo@0: return our_ip; paulo@0: } paulo@0: paulo@0: /* paulo@0: * Begin a node connection with the peer on the specified TCPC. paulo@0: * paulo@0: * We arrive here from either an incoming or outgoing connection. paulo@0: * This is the entrance point to the main packet-reading loop. paulo@0: * paulo@0: * After setting up the connection, we send the node a ping. paulo@0: * If it doesn't respond after a timeout, we will destroy the paulo@0: * connection. paulo@0: */ paulo@0: void gnutella_start_connection (int fd, input_id id, TCPC *c) paulo@0: { paulo@0: GtPacket *ping; paulo@0: GtNode *node; paulo@0: paulo@0: node = GT_NODE(c); paulo@0: assert (GT_CONN(node) == c); paulo@0: paulo@0: /* remove the old input handler first -- need to before sending data */ paulo@0: input_remove (id); paulo@0: paulo@0: if (net_sock_error (c->fd)) paulo@0: { paulo@0: if (HANDSHAKE_DEBUG) paulo@0: gt_node_error (c, NULL); paulo@0: paulo@0: gt_node_disconnect (c); paulo@0: return; paulo@0: } paulo@0: paulo@0: /* if this is the crawler, disconnect */ paulo@0: if (dataset_lookupstr (GT_NODE(c)->hdr, "crawler")) paulo@0: { paulo@0: if (HANDSHAKE_DEBUG) paulo@0: GT->DBGSOCK (GT, c, "closing crawler connection"); paulo@0: paulo@0: gt_node_disconnect (c); paulo@0: return; paulo@0: } paulo@0: paulo@0: if (!(node->rx_stack = gt_rx_stack_new (node, c, node->rx_inflated))) paulo@0: { paulo@0: if (HANDSHAKE_DEBUG) paulo@0: GT->DBGSOCK (GT, c, "error allocating rx_stack"); paulo@0: paulo@0: gt_node_disconnect (c); paulo@0: return; paulo@0: } paulo@0: paulo@0: if (!(node->tx_stack = gt_tx_stack_new (c, node->tx_deflated))) paulo@0: { paulo@0: if (HANDSHAKE_DEBUG) paulo@0: GT->DBGSOCK (GT, c, "error allocating tx stack"); paulo@0: paulo@0: gt_node_disconnect (c); paulo@0: return; paulo@0: } paulo@0: paulo@0: /* determine the other node's opinion of our IP address */ paulo@0: node->my_ip = get_self_ip (c); paulo@0: paulo@0: /* determine the other ends port */ paulo@0: peer_addr (c->fd, NULL, &node->peer_port); paulo@0: paulo@0: if (HANDSHAKE_DEBUG) paulo@0: { paulo@0: GT->DBGSOCK (GT, c, "self IP=[%s]", net_ip_str (node->my_ip)); paulo@0: GT->DBGSOCK (GT, c, "peer port=%hu", node->peer_port); paulo@0: } paulo@0: paulo@0: if (!(ping = gt_packet_new (GT_MSG_PING, 1, NULL))) paulo@0: { paulo@0: gt_node_disconnect (c); paulo@0: return; paulo@0: } paulo@0: paulo@0: /* set the state as intermediately connecting and mark the node connected paulo@0: * only when it replies to our ping */ paulo@0: gt_node_state_set (node, GT_NODE_CONNECTING_2); paulo@0: paulo@0: /* give the connection some more time */ paulo@0: gnutella_set_handshake_timeout (c, TIMEOUT_3 * SECONDS); paulo@0: paulo@0: /* paulo@0: * Setup our packet handlers, for both receiving and sending packets. paulo@0: */ paulo@0: gt_rx_stack_set_handler (node->rx_stack, paulo@0: (GtRxStackHandler)recv_packet, paulo@0: (GtRxStackCleanup)cleanup_node_rx, paulo@0: node); paulo@0: paulo@0: gt_tx_stack_set_handler (node->tx_stack, paulo@0: (GtTxStackCleanup)cleanup_node_tx, paulo@0: node); paulo@0: paulo@0: /* send first ping */ paulo@0: gt_packet_send (c, ping); paulo@0: gt_packet_free (ping); paulo@0: paulo@0: /* send MessagesSupported Vendor message, if this node supports it */ paulo@0: gt_vmsg_send_supported (node); paulo@0: }