From 10b625e18030144f30a68556321a4c3d11fa7e25 Mon Sep 17 00:00:00 2001 From: Aris Adamantiadis Date: Sun, 8 Nov 2009 23:42:41 +0100 Subject: [PATCH] First lines of experimental pcap output support This will serve to debug packets right under wireshark ! --- DefineOptions.cmake | 1 + config.h.cmake | 3 + include/libssh/buffer.h | 1 + include/libssh/pcap.h | 30 +++++ libssh/CMakeLists.txt | 1 + libssh/buffer.c | 15 +++ libssh/pcap.c | 291 ++++++++++++++++++++++++++++++++++++++++ tests/Makefile | 5 +- tests/test_pcap.c | 50 +++++++ 9 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 include/libssh/pcap.h create mode 100644 libssh/pcap.c create mode 100644 tests/test_pcap.c diff --git a/DefineOptions.cmake b/DefineOptions.cmake index bec65614..f0697960 100644 --- a/DefineOptions.cmake +++ b/DefineOptions.cmake @@ -6,3 +6,4 @@ option(WITH_STATIC_LIB "Build with a static library" OFF) option(WITH_DEBUG_CRYPTO "Build with cryto debug output" OFF) option(WITH_DEBUG_CALLTRACE "Build with calltrace debug output" ON) option(WITH_GCRYPT "Compile against libgcrypt" OFF) +option(WITH_PCAP "Compile with Pcap generation support" OFF) diff --git a/config.h.cmake b/config.h.cmake index b483ea1c..8ef29ff2 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -83,6 +83,9 @@ /* Define to 1 if you want to enable debug output for crypto functions */ #cmakedefine DEBUG_CRYPTO 1 +/* Define to 1 if you want to enable pcap output support (experimental) */ +#cmakedefine WITH_PCAP 1 + /* Define to 1 if you want to enable calltrace debug output */ #cmakedefine DEBUG_CALLTRACE 1 diff --git a/include/libssh/buffer.h b/include/libssh/buffer.h index d8055fd7..1219cead 100644 --- a/include/libssh/buffer.h +++ b/include/libssh/buffer.h @@ -33,6 +33,7 @@ struct ssh_buffer_struct { int buffer_add_ssh_string(ssh_buffer buffer, ssh_string string); int buffer_add_u8(ssh_buffer buffer, uint8_t data); +int buffer_add_u16(ssh_buffer buffer, uint16_t data); int buffer_add_u32(ssh_buffer buffer, uint32_t data); int buffer_add_u64(ssh_buffer buffer, uint64_t data); int buffer_add_data(ssh_buffer buffer, const void *data, uint32_t len); diff --git a/include/libssh/pcap.h b/include/libssh/pcap.h new file mode 100644 index 00000000..c0ef73fe --- /dev/null +++ b/include/libssh/pcap.h @@ -0,0 +1,30 @@ +#ifndef PCAP_H_ +#define PCAP_H_ + +#include "config.h" + +#ifdef WITH_PCAP +typedef struct ssh_pcap_context_struct* ssh_pcap_context; +typedef struct ssh_pcap_file_struct* ssh_pcap_file; + +ssh_pcap_file ssh_pcap_file_new(void); +int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename); +int ssh_pcap_file_close(ssh_pcap_file pcap); +void ssh_pcap_free(ssh_pcap_file pcap); + +/* to be removed from here after tests */ +int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, u_int32_t original_len); + +ssh_pcap_context ssh_pcap_context_new(ssh_session session); + +enum ssh_pcap_direction{ + SSH_PCAP_DIR_IN, + SSH_PCAP_DIR_OUT +}; +void ssh_pcap_context_set_file(ssh_pcap_context, ssh_pcap_file); +int ssh_pcap_context_write(ssh_pcap_context,enum ssh_pcap_direction direction, void *data, + u_int32_t len, u_int32_t origlen); + +#endif /* WITH_PCAP */ +#endif /* PCAP_H_ */ +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/CMakeLists.txt b/libssh/CMakeLists.txt index 7a4093da..11ddcff5 100644 --- a/libssh/CMakeLists.txt +++ b/libssh/CMakeLists.txt @@ -93,6 +93,7 @@ set(libssh_SRCS misc.c options.c packet.c + pcap.c poll.c session.c scp.c diff --git a/libssh/buffer.c b/libssh/buffer.c index a24dc688..761c51b0 100644 --- a/libssh/buffer.c +++ b/libssh/buffer.c @@ -174,6 +174,7 @@ int buffer_add_ssh_string(struct ssh_buffer_struct *buffer, return 0; } + /** \internal * \brief add a 32 bits unsigned integer to the tail of buffer * \param buffer buffer @@ -188,6 +189,20 @@ int buffer_add_u32(struct ssh_buffer_struct *buffer,uint32_t data){ return 0; } +/** \internal + * \brief add a 16 bits unsigned integer to the tail of buffer + * \param buffer buffer + * \param data 16 bits integer + * \return 0 on success, -1 on error. + */ +int buffer_add_u16(struct ssh_buffer_struct *buffer,uint16_t data){ + if (buffer_add_data(buffer, &data, sizeof(data)) < 0) { + return -1; + } + + return 0; +} + /** \internal * \brief add a 64 bits unsigned integer to the tail of buffer * \param buffer buffer diff --git a/libssh/pcap.c b/libssh/pcap.c new file mode 100644 index 00000000..3c45c1e7 --- /dev/null +++ b/libssh/pcap.c @@ -0,0 +1,291 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/* pcap.c */ +/** \defgroup ssh_pcap SSH-pcap + * \brief libssh pcap file generation + * + * \addtogroup ssh_pcap + * @{ */ + +#include +#include + +#include "config.h" +#include "libssh/libssh.h" +#include "libssh/pcap.h" +#include "libssh/session.h" +#include "libssh/buffer.h" + +#ifdef WITH_PCAP +/* The header of a pcap file is the following. We are not going to make it + * very complicated. + * Just for information. + */ +struct pcap_hdr_s { + u_int32_t magic_number; /* magic number */ + u_int16_t version_major; /* major version number */ + u_int16_t version_minor; /* minor version number */ + int32_t thiszone; /* GMT to local correction */ + u_int32_t sigfigs; /* accuracy of timestamps */ + u_int32_t snaplen; /* max length of captured packets, in octets */ + u_int32_t network; /* data link type */ +}; + +#define PCAP_MAGIC 0xa1b2c3d4 +#define PCAP_VERSION_MAJOR 2 +#define PCAP_VERSION_MINOR 4 + +#define DLT_RAW 12 /* raw IP */ + +/* TCP flags */ +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 + +/* The header of a pcap packet. + * Just for information. + */ +struct pcaprec_hdr_s { + u_int32_t ts_sec; /* timestamp seconds */ + u_int32_t ts_usec; /* timestamp microseconds */ + u_int32_t incl_len; /* number of octets of packet saved in file */ + u_int32_t orig_len; /* actual length of packet */ +}; + +/** @private + * @brief a pcap context expresses the state of a pcap dump + * in a SSH session only. Multiple pcap contexts may be used into + * a single pcap file. + */ + +struct ssh_pcap_context_struct { + ssh_session session; + ssh_pcap_file file; + /* All of these informations are useful to generate + * the dummy IP and TCP packets + */ + u_int32_t ipsource; + u_int32_t ipdest; + u_int16_t portsource; + u_int16_t portdest; + u_int32_t outsequence; + u_int32_t insequence; +}; + +/** @private + * @brief a pcap file expresses the state of a pcap file which may + * contain several streams. + */ +struct ssh_pcap_file_struct { + FILE *output; +}; + +/** + * @brief create a new ssh_pcap_file object + */ +ssh_pcap_file ssh_pcap_file_new(){ + return malloc(sizeof(struct ssh_pcap_file_struct)); +} + +/** @internal + * @brief writes a packet on file + */ +static int ssh_pcap_file_write(ssh_pcap_file pcap, ssh_buffer packet){ + int err; + uint32_t len; + if(pcap == NULL || pcap->output==NULL) + return SSH_ERROR; + len=buffer_get_len(packet); + err=fwrite(buffer_get(packet),len,1,pcap->output); + if(err<0) + return SSH_ERROR; + else + return SSH_OK; +} + +/** @internal + * @brief prepends a packet with the pcap header and writes packet + * on file + */ +int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, u_int32_t original_len){ + ssh_buffer header=buffer_new(); + struct timeval now; + int err; + if(header == NULL) + return SSH_ERROR; + gettimeofday(&now,NULL); + buffer_add_u32(header,htonl(now.tv_sec)); + buffer_add_u32(header,htonl(now.tv_usec)); + buffer_add_u32(header,htonl(buffer_get_len(packet))); + buffer_add_u32(header,htonl(original_len)); + buffer_add_buffer(header,packet); + err=ssh_pcap_file_write(pcap,header); + buffer_free(header); + return err; +} + +/** + * @brief opens a new pcap file and create header + */ +int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename){ + ssh_buffer header; + int err; + if(pcap == NULL) + return SSH_ERROR; + if(pcap->output){ + fclose(pcap->output); + pcap->output=NULL; + } + pcap->output=fopen(filename,"wb"); + if(pcap->output==NULL) + return SSH_ERROR; + header=buffer_new(); + if(header==NULL) + return SSH_ERROR; + buffer_add_u32(header,htonl(PCAP_MAGIC)); + buffer_add_u16(header,htons(PCAP_VERSION_MAJOR)); + buffer_add_u16(header,htons(PCAP_VERSION_MINOR)); + /* currently hardcode GMT to 0 */ + buffer_add_u32(header,htonl(0)); + /* accuracy */ + buffer_add_u32(header,htonl(0)); + /* size of the biggest packet */ + buffer_add_u32(header,htonl(MAX_PACKET_LEN)); + /* we will write sort-of IP */ + buffer_add_u32(header,htonl(DLT_RAW)); + err=ssh_pcap_file_write(pcap,header); + buffer_free(header); + return err; +} + +int ssh_pcap_file_close(ssh_pcap_file pcap){ + int err; + if(pcap ==NULL || pcap->output==NULL) + return SSH_ERROR; + err=fclose(pcap->output); + pcap->output=NULL; + if(err != 0) + return SSH_ERROR; + else + return SSH_OK; +} + +void ssh_pcap_free(ssh_pcap_file pcap){ + ssh_pcap_file_close(pcap); + SAFE_FREE(pcap); +} + + +/** @internal + * @brief allocates a new ssh_pcap_context object + */ + +ssh_pcap_context ssh_pcap_context_new(ssh_session session){ + ssh_pcap_context ctx=malloc(sizeof(struct ssh_pcap_context_struct)); + if(ctx==NULL){ + ssh_set_error_oom(session); + return NULL; + } + ZERO_STRUCTP(ctx); + ctx->session=session; + return ctx; +} + +void ssh_pcap_context_set_file(ssh_pcap_context ctx, ssh_pcap_file pcap){ + ctx->file=pcap; +} + +int ssh_pcap_context_write(ssh_pcap_context ctx,enum ssh_pcap_direction direction + , void *data, u_int32_t len, u_int32_t origlen){ + ssh_buffer ip=buffer_new(); + int err; + if(ip==NULL){ + ssh_set_error_oom(ctx->session); + return SSH_ERROR; + } + /* build an IP packet */ + /* V4, 20 bytes */ + buffer_add_u8(ip,4 << 4 | 5); + /* tos */ + buffer_add_u8(ip,0); + /* total len */ + buffer_add_u16(ip,htons(origlen + 40)); + /* id */ + buffer_add_u16(ip,htons(1)); + /* fragment offset */ + buffer_add_u16(ip,htons(0)); + /* TTL */ + buffer_add_u8(ip,64); + /* protocol TCP=6 */ + buffer_add_u8(ip,6); + /* checksum */ + buffer_add_u16(ip,0); + if(direction==SSH_PCAP_DIR_OUT){ + buffer_add_u32(ip,ctx->ipsource); + buffer_add_u32(ip,ctx->ipdest); + } else { + buffer_add_u32(ip,ctx->ipdest); + buffer_add_u32(ip,ctx->ipsource); + } + /* TCP */ + if(direction==SSH_PCAP_DIR_OUT){ + buffer_add_u16(ip,ntohs(ctx->portsource)); + buffer_add_u16(ip,ntohs(ctx->portdest)); + } else { + buffer_add_u16(ip,ntohs(ctx->portdest)); + buffer_add_u16(ip,ntohs(ctx->portsource)); + } + /* sequence number */ + if(direction==SSH_PCAP_DIR_OUT){ + buffer_add_u32(ip,ntohl(ctx->outsequence)); + } else { + buffer_add_u32(ip,ntohl(ctx->insequence)); + } + /* ack number */ + if(direction==SSH_PCAP_DIR_OUT){ + buffer_add_u32(ip,ntohl(ctx->insequence)); + } else { + buffer_add_u32(ip,ntohl(ctx->outsequence)); + } + /* header len */ + buffer_add_u8(ip,5 << 4); + /* flags */ + buffer_add_u8(ip,TH_PUSH | TH_ACK); + /* window */ + buffer_add_u16(ip,htons(65535)); + /* checksum */ + buffer_add_u16(ip,htons(0)); + /* urgent data ptr */ + buffer_add_u16(ip,0); + /* actual data */ + buffer_add_data(ip,data,len); + err=ssh_pcap_file_write_packet(ctx->file,ip,origlen + 40); + buffer_free(ip); + return err; +} + +#endif /* WITH_PCAP */ +/** @} */ +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/tests/Makefile b/tests/Makefile index 8483856e..1ec30d6a 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,4 +1,4 @@ -all: test_tunnel test_exec +all: test_tunnel test_exec test_pcap CFLAGS=-I../include/ -g -Wall LDFLAGS=-lssh -L../build/libssh/ @@ -8,5 +8,8 @@ test_tunnel: test_tunnel.o authentication.o connection.o test_exec: test_exec.o authentication.o connection.o gcc -o $@ $^ $(LDFLAGS) +test_pcap: test_pcap.o + gcc -o $@ $^ $(LDFLAGS) + clean: rm -f *.o test_tunnel diff --git a/tests/test_pcap.c b/tests/test_pcap.c new file mode 100644 index 00000000..994ae4bf --- /dev/null +++ b/tests/test_pcap.c @@ -0,0 +1,50 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/* Simple test for the pcap functions */ + +#include +#include +#include + +#include +#include +#include + +int main(int argc, char **argv){ + ssh_pcap_file pcap; + ssh_pcap_context ctx; + ssh_buffer buffer=buffer_new(); + char *str="Hello, this is a test string to test the capabilities of the" + "pcap file writer."; + printf("Simple pcap tester\n"); + pcap=ssh_pcap_file_new(); + if(ssh_pcap_file_open(pcap,"test.cap") != SSH_OK){ + printf("error happened\n"); + return EXIT_FAILURE; + } + buffer_add_data(buffer,str,strlen(str)); + ctx=ssh_pcap_context_new(NULL); + ssh_pcap_context_set_file(ctx,pcap); + ssh_pcap_context_write(ctx,SSH_PCAP_DIR_OUT,str,strlen(str),strlen(str)); + + return EXIT_SUCCESS; +}