/* OpenIDA - Outil d'analyse de fichiers binaires
 * tcp.c - gestion des connexions TCP aux serveurs JDWP.
 *
 * Copyright (C) 2010-2012 Cyrille Bagard
 *
 *  This file is part of OpenIDA.
 *
 *  OpenIDA is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  OpenIDA 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "tcp.h"


#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>


#include <i18n.h>


#include "packet.h"
#include "misc/header.h"
#include "sets/list.h"
#include "../stream-int.h"
#include "../../common/net.h"
#include "../../gui/panels/log.h"



/* Flux de communication TCP avec un serveur JDWP (instance) */
struct _GJdwpTcpClient
{
    GDebugStream parent;                    /* A laisser en premier        */

    char *server;                           /* Serveur à contacter         */
    char *port;                             /* Port de connexion           */
    int fd;                                 /* Flux ouvert en L./E.        */

};


/* Flux de communication TCP avec un serveur JDWP (classe) */
struct _GJdwpTcpClientClass
{
    GDebugStreamClass parent;               /* A laisser en premier        */

};


/* Initialise la classe des flux de communication JDWP over TCP. */
static void g_jdwp_tcp_client_class_init(GJdwpTcpClientClass *);

/* Initialise une instance de flux de communication avec JDWP. */
static void g_jdwp_tcp_client_init(GJdwpTcpClient *);

/* Etablit de façon effective une connexion à la cible. */
static bool g_jdwp_tcp_client_connect(GJdwpTcpClient *);

/* Attend le signalement de données à traiter. */
static bool g_jdwp_tcp_client_poll(GJdwpTcpClient *);

/* Envoie un paquet de données à un serveur de débogage. */
static bool g_jdwp_tcp_client_send_packet(GJdwpTcpClient *, const GJdwpPacket *);

/* Réceptionne un paquet de données d'un serveur de débogage. */
static bool g_jdwp_tcp_client_recv_packet(GJdwpTcpClient *, GJdwpPacket *);

/* Libère le contenu alloué d'un paquet de débogage. */
static void g_jdwp_tcp_client_free_packet(GJdwpTcpClient *, GJdwpPacket *);



/* Indique le type défini pour un flux de communication TCP avec un serveur JDWP. */
G_DEFINE_TYPE(GJdwpTcpClient, g_jdwp_tcp_client, G_TYPE_DEBUG_STREAM);


/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des flux de communication JVDP over TCP.*
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_jdwp_tcp_client_class_init(GJdwpTcpClientClass *klass)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : client = instance à initialiser.                             *
*                                                                             *
*  Description : Initialise une instance de flux de communication avec JDWP.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_jdwp_tcp_client_init(GJdwpTcpClient *client)
{
    GDebugStream *stream;                   /* Version parente             */

    stream = G_DEBUG_STREAM(client);

    stream->connect = (debug_connect_fc)g_jdwp_tcp_client_connect;

    stream->poll = (debug_poll_fc)g_jdwp_tcp_client_poll;
    stream->send_packet = (debug_pkt_op_fc)g_jdwp_tcp_client_send_packet;
    stream->recv_packet = (debug_pkt_op_fc)g_jdwp_tcp_client_recv_packet;
    stream->free_packet = (debug_free_pkt_fc)g_jdwp_tcp_client_free_packet;

    stream->pkt_type = G_TYPE_JDWP_PACKET;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : server = nom ou adresse du serveur à contacter.              *
*                port   = port de connexion.                                  *
*                                                                             *
*  Description : Crée une nouvelle connexion TCP à un serveur JDWP.           *
*                                                                             *
*  Retour      : Adresse de la structure mise en place.                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GDebugStream *g_jdwp_tcp_client_new(const char *server, const char *port)
{
    GJdwpTcpClient *result;                 /* Structure à retourner       */

    result = g_object_new(G_TYPE_JDWP_TCP_CLIENT, NULL);

    result->server = strdup(server);
    result->port = strdup(port);
    result->fd = -1;

    return G_DEBUG_STREAM(result);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : client = paramètres de connexion au serveur JDWP.            *
*                                                                             *
*  Description : Etablit de façon effective une connexion à la cible.         *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_jdwp_tcp_client_connect(GJdwpTcpClient *client)
{
    struct sockaddr_in addr;                /* Infos de connexion distante */
    int sock;                               /* Flux ouvert à construire    */
    char handshake[15];                     /* Poignée de main chaleureuse */

    sock = connect_via_tcp(client->server, client->port, &addr);
    if (sock == -1)
    {
        log_variadic_message(LMT_ERROR, _("Error while connecting to the JDWP server at %s:%s."),
                             //printf("Echec de connexion au serveur JDWP sur %s:%s\n",
                             client->server, client->port);
        return false;
    }

    log_variadic_message(LMT_PROCESS, _("Connected to %s:%hd."),
                         client->server, ntohs(addr.sin_port));

    if (send(sock, "JDWP-Handshake", 14, 0) != 14)
        goto gjtcc_error;

    if (recv(sock, handshake, 14, 0) != 14)
        goto gjtcc_error;

    if (strncmp(handshake, "JDWP-Handshake", 14) != 0)
        goto gjtcc_error;

    client->fd = sock;

    return true;

 gjtcc_error:

    log_simple_message(LMT_ERROR, _("Failure in the first JDWP handshake."));

    close(sock);

    return false;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : client = paramètres de connexion au serveur JDWP.            *
*                                                                             *
*  Description : Attend le signalement de données à traiter.                  *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_jdwp_tcp_client_poll(GJdwpTcpClient *client)
{
    bool result;                            /* Statut à faire remonter     */
    fd_set rfds;                            /* Liste des flux à surveiller */
    int ret;                                /* Bilan d'un appel            */

    result = false;

    FD_ZERO(&rfds);
    FD_SET(client->fd, &rfds);

    ret = select(client->fd + 1, &rfds, NULL, NULL, NULL);

    switch (ret)
    {
            case -1:
                perror("select()");
                break;

            case 0:
                /* ?! */
                break;

            default:
                result = true;
                break;

    }

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : client = flux ouvert en écriture à utiliser.                 *
*                packet = zone mémoire à parcourir.                           *
*                                                                             *
*  Description : Envoie un paquet de données à un serveur de débogage.        *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_jdwp_tcp_client_send_packet(GJdwpTcpClient *client, const GJdwpPacket *packet)
{
    struct iovec iov[UIO_MAXIOV];           /* Table de vecteurs à écrire  */
    int iovcnt;                             /* Quantité de champs valides  */
    int i;                                  /* Boucle de parcours          */

#if 0
    jdwp_header *header;                    /* En-tête à reconstituer      */

    header = g_jdwp_packet_get_header(packet);
    printf(" <JDWP> send %p :: %u / %hhu.%hhu (%u)\n", packet, header->id, header->set,
           header->command, header->length);
#endif

    g_debug_packet_vectorize(G_DEBUG_PACKET(packet), iov, &iovcnt);

    for (i = 0; i < iovcnt; i++)
        if (send(client->fd, iov[i].iov_base, iov[i].iov_len, 0) != iov[i].iov_len)
            return false;

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : client = flux ouvert en lecture à utiliser.                  *
*                packet = zone mémoire à remplir. [OUT]                       *
*                                                                             *
*  Description : Réceptionne un paquet de données d'un serveur de débogage.   *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_jdwp_tcp_client_recv_packet(GJdwpTcpClient *client, GJdwpPacket *packet)
{
    bin_t *hblob;                           /* Contenu encodé en B.E.      */
    jdwp_header *header;                    /* En-tête à reconstituer      */
    uint32_t length;                        /* Taille de la charge utile   */
    bin_t *pblob;                           /* Contenu encodé en B.E.      */

    hblob = g_jdwp_packet_get_hblob(packet);

    if (recv(client->fd, hblob, sizeof(jdwp_header), 0) != sizeof(jdwp_header))
        return false;

    if (!g_jdwp_packet_parse_header(packet))
        return false;

    header = g_jdwp_packet_get_header(packet);
    length = header->length - sizeof(jdwp_header);

    //printf(" <JDWP> recv %p :: %u / %hu (%u)\n", packet, header->id, header->error, header->length);

    pblob = g_jdwp_packet_get_pblob(packet);

    if (recv(client->fd, pblob, length, 0) != length)
        return false;

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : client = flux ouvert inutile.                                *
*                packet = zone mémoire à libérer.                             *
*                                                                             *
*  Description : Libère le contenu alloué d'un paquet de débogage.            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_jdwp_tcp_client_free_packet(GJdwpTcpClient *client, GJdwpPacket *packet)
{
    g_jdwp_packet_free_payload(packet);

}