/* Chrysalide - Outil d'analyse de fichiers binaires
* stream.c - gestion des connexions aux serveurs GDB.
*
* Copyright (C) 2009-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 "stream.h"
#include
#include
#include
#include
#include "stream-int.h"
#include "../../common/dllist.h"
/* Initialise la classe des flux de communication avec GDB. */
static void g_gdb_stream_class_init(GGdbStreamClass *);
/* Initialise une instance de flux de communication avec GDB. */
static void g_gdb_stream_init(GGdbStream *);
/* Envoie un acquittement pour la dernière réception. */
static bool gdb_stream_ack(GGdbStream *);
/* Ecoute une connexion à un serveur GDB. */
static void *gdb_stream_thread(GGdbStream *);
/* Réceptionne un paquet d'un serveur GDB. */
static bool g_gdb_stream_read_packet(GGdbStream *, GGdbPacket *);
/* Indique le type défini pour un flux de communication avec un serveur GDB. */
G_DEFINE_TYPE(GGdbStream, g_gdb_stream, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des flux de communication avec GDB. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_gdb_stream_class_init(GGdbStreamClass *klass)
{
}
/******************************************************************************
* *
* Paramètres : stream = instance à initialiser. *
* *
* Description : Initialise une instance de flux de communication avec GDB. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_gdb_stream_init(GGdbStream *stream)
{
g_mutex_init(&stream->free_mutex);
g_cond_init(&stream->recv_cond);
g_mutex_init(&stream->recv_mutex);
}
/******************************************************************************
* *
* Paramètres : stream = instance à réellement lancer. *
* *
* Description : Lance l'écoute d'un flux de communication avec GDB. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_gdb_stream_listen(GGdbStream *stream)
{
bool result; /* Bilan final à retourner */
result = true;
if (!g_thread_new("chrysalide_gdb_stream", (GThreadFunc)gdb_stream_thread, stream))
result = false;
return result;
}
/******************************************************************************
* *
* Paramètres : stream = encadrement associée à l'opération. *
* *
* Description : Envoie un acquittement pour la dernière réception. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool gdb_stream_ack(GGdbStream *stream)
{
bool result; /* Bilan à retourner */
GGdbPacket *packet; /* Paquet à envoyer */
packet = g_gdb_stream_get_free_packet(stream);
g_gdb_packet_start_new_command(packet);
g_gdb_packet_append(packet, "+");
result = g_gdb_stream_send_packet(stream, packet);
g_gdb_stream_mark_packet_as_free(stream, packet);
return result;
}
/******************************************************************************
* *
* Paramètres : stream = encadrement associée à l'opération. *
* *
* Description : Ecoute une connexion à un serveur GDB. *
* *
* Retour : ??? *
* *
* Remarques : - *
* *
******************************************************************************/
static void *gdb_stream_thread(GGdbStream *stream)
{
fd_set rfds; /* Liste des flux à surveiller */
int ret; /* Bilan d'un appel */
GGdbPacket *packet; /* Nouveau paquet reçu */
while (1)
{
FD_ZERO(&rfds);
FD_SET(stream->fd, &rfds);
ret = select(stream->fd + 1, &rfds, NULL, NULL, NULL);
printf("ret :: %d\n", ret);
switch (ret)
{
case -1:
perror("select()");
break;
case 0:
break;
default:
packet = g_gdb_stream_get_free_packet(stream);
g_gdb_packet_start_new_command(packet);
if (g_gdb_stream_read_packet(stream, packet))
{
/* Acquittement */
if (!gdb_stream_ack(stream)) goto bad_recv;
/* On conserve le résultat */
g_mutex_lock(&stream->recv_mutex);
g_gdb_packet_push(&stream->recv_packets, packet);
g_mutex_unlock(&stream->recv_mutex);
g_cond_signal(&stream->recv_cond);
}
bad_recv:
printf("bad things happend...\n");
g_gdb_stream_mark_packet_as_free(stream, packet);
break;
}
}
return NULL;
}
/******************************************************************************
* *
* Paramètres : stream = flux de communication avec GDB à consulter. *
* *
* Description : Fournit un paquet prêt à emploi. *
* *
* Retour : Paquet prêt à emploi. *
* *
* Remarques : - *
* *
******************************************************************************/
GGdbPacket *g_gdb_stream_get_free_packet(GGdbStream *stream)
{
GGdbPacket *result; /* Paquet à retourner */
g_mutex_lock(&stream->free_mutex);
if (dl_list_empty(stream->free_packets))
result = g_gdb_packet_new();
else
result = g_gdb_packet_pop(&stream->free_packets);
g_mutex_unlock(&stream->free_mutex);
return result;
}
/******************************************************************************
* *
* Paramètres : stream = flux de communication avec GDB à mettre à jour. *
* packet = paquet à considérer comme disponible. *
* *
* Description : Place un paquet en attente d'une future utilisation. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_gdb_stream_mark_packet_as_free(GGdbStream *stream, GGdbPacket *packet)
{
g_mutex_lock(&stream->free_mutex);
g_gdb_packet_push(&stream->free_packets, packet);
g_mutex_unlock(&stream->free_mutex);
}
/******************************************************************************
* *
* Paramètres : stream = flux ouvert en lecture à utiliser. *
* packet = données à recevoir. *
* *
* Description : Réceptionne un paquet d'un serveur GDB. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool g_gdb_stream_read_packet(GGdbStream *stream, GGdbPacket *packet)
{
bool result; /* Bilan à renvoyer */
char tmp[3]; /* Tampon de réception */
uint8_t checksum; /* Contrôle d'intégrité */
result = stream->recv_byte(stream, tmp);
if (tmp[0] != '$') return false;
tmp[1] = '\0';
while ((result = stream->recv_byte(stream, tmp)))
{
if (tmp[0] == '#') break;
else g_gdb_packet_append(packet, tmp);
}
if (result)
{
result = stream->recv_byte(stream, &tmp[0]);
result &= stream->recv_byte(stream, &tmp[1]);
tmp[2] = 0;
checksum = strtol(tmp, NULL, 16);
}
if (result)
result = g_gdb_packet_verify_checksum(packet, checksum);
if (result)
result = g_gdb_packet_decode(packet);
return result;
}
/******************************************************************************
* *
* Paramètres : stream = flux ouvert en écriture à mettre à jour. *
* packet = données à transmettre. *
* *
* Description : Envoie un paquet à un serveur GDB. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_gdb_stream_send_packet(GGdbStream *stream, GGdbPacket *packet)
{
bool result; /* Bilan à renvoyer */
const char *data; /* Données à envoyer */
size_t len; /* Quantité de ces données */
uint8_t checksum; /* Contrôle d'intégrité */
char tmp[3]; /* Impression du checksum */
result = stream->send_data(stream, "$", 1);
g_gdb_packet_compute_checksum(packet);
g_gdb_packet_get_data(packet, &data, &len, &checksum);
result &= stream->send_data(stream, data, len);
result = stream->send_data(stream, "#", 1);
snprintf(tmp, 3, "%hhx", checksum);
result &= stream->send_data(stream, tmp, 2);
return result;
}
/******************************************************************************
* *
* Paramètres : stream = flux de communication avec GDB à consulter. *
* *
* Description : Fournit un paquet reçu d'un serveur GDB. *
* *
* Retour : Paquet GDB. *
* *
* Remarques : - *
* *
******************************************************************************/
GGdbPacket *g_gdb_stream_recv_packet(GGdbStream *stream)
{
GGdbPacket *result; /* Paquet à retourner */
g_mutex_lock(&stream->recv_mutex);
if (dl_list_empty(stream->recv_packets))
g_cond_wait(&stream->recv_cond, &stream->recv_mutex);
result = g_gdb_packet_pop(&stream->recv_packets);
g_mutex_unlock(&stream->recv_mutex);
return result;
}