/* Chrysalide - Outil d'analyse de fichiers binaires
* tcp.c - gestion des connexions TCP aux serveurs JDWP.
*
* Copyright (C) 2010-2017 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide 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.
*
* Chrysalide 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 .
*/
#include "tcp.h"
#include
#include
#include
#include
#include
#include
#include
#include "packet.h"
#include "misc/header.h"
#include "sets/list.h"
#include "../stream-int.h"
#include "../../common/net.h"
#include "../../core/logs.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(" 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(" 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);
}