-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpcap.c
361 lines (327 loc) · 13.8 KB
/
pcap.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/*
*@file pcap Basado en ejemplo.pcap dado por el ayudante
* y en el sniffer implementado por tcpdump (http://www.tcpdump.org)
*@brief Programa que permite capturar paquetes, ya sea uno o
* indefinidos y lo muestra en pantalla
*@author Vilchis Domínguez Miguel Alonso
*@author Bernal Cedillo Enrique Antonio
*@author Velasquez Garzon Angie Daniela
*/
/*++++++++++++++++Compilacion y uso +++++++++++++++++++++++++
*Para instalar libpcap: apt-get install libpcap-dev
*Para compilar el programa la linea de comandos corrrespondiente
*es: gcc ejemplo_pcap.c -o lab -lpcap
*Y como super usuario se ejecuta ./lab
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pcap.h>
#include <arpa/inet.h>
#include "pcap.h"
// Contador global de indice de paquetes
int indice = 0;
/**************************************************************
*@brief Dada un buffer de caracteres (a) y una subcadena (b)
* regresa verdadero si el buffer inicia con la subcadena
* regresa falso en otro caso
***************************************************************/
int empiezaCon(const char *a, const char *b){
if(strncmp(a, b, strlen(b)) == 0)
return 1;
return 0;
}
/**************************************************************
*@brief Dado un apuntador de un buffer de caracteres,
* imprime todo el contenido hasta encontrar el caracter de escape
* utilizado en TCP para separar los campos y argumentos de los paquetes
***************************************************************/
void imprimeHastaEscape(char *string){
printf("--) ");
const u_char *ch;
ch = string;
while(isprint(*ch)){
printf("%c", *ch);
ch++;
}
printf("\n");
}
/**************************************************************
*@brief Dado un buffer de caracteres imprime el contenido de acuerdo
* al formato requerido para los paquetes de tipo RESPONSE
***************************************************************/
void imprimeResponse(const char *buffer){
// Status code
printf("--) Status code: %c%c%c\n", buffer[9], buffer[10], buffer[11]);
char *str_connection = strstr(buffer, "Connection:");
if (str_connection == NULL)
printf("--) Connection: Campo no definido\n");
else
imprimeHastaEscape(str_connection);
char *str_date = strstr(buffer, "Date:");
if (str_date == NULL)
printf("--) Date: Campo no definido\n");
else
imprimeHastaEscape(str_date);
char *str_server = strstr(buffer, "Server:");
if (str_server == NULL)
printf("--) Server: Campo no definido\n");
else
imprimeHastaEscape(str_server);
char *str_last_modified = strstr(buffer, "Last-Modified:");
if (str_last_modified == NULL)
printf("--) Last-Modified: Campo no definido\n");
else
imprimeHastaEscape(str_last_modified);
char *str_content_length = strstr(buffer, "Content-Length:");
if (str_content_length == NULL)
printf("--) Content-Length: Campo no definido\n");
else
imprimeHastaEscape(str_content_length);
char *str_content_type = strstr(buffer, "Content-Type:");
if (str_content_type == NULL)
printf("--) Content-Type: Campo no definido\n");
else
imprimeHastaEscape(str_content_type);
}
/**************************************************************
*@brief Dado un buffer de caracteres imprime el contenido de acuerdo
* al formato requerido para los paquetes de tipo REQUEST
***************************************************************/
void imprimeRequest(const char *buffer){
if(empiezaCon(buffer, "POST"))
printf("--) Method: POST\n");
if(empiezaCon(buffer, "GET"))
printf("--) Method: GET\n");
if(empiezaCon(buffer, "HEAD"))
printf("--) Method: HEAD\n");
if(empiezaCon(buffer, "OPTIONS"))
printf("--) Method: OPTIONS\n");
if(empiezaCon(buffer, "PUT"))
printf("--) Method: PUT\n");
if(empiezaCon(buffer, "DELETE"))
printf("--) Method: DELETE\n");
if(empiezaCon(buffer, "TRACE"))
printf("--) Method: TRACE\n");
if(empiezaCon(buffer, "CONNECT"))
printf("--) Method: CONNECT\n");
char *str_host = strstr(buffer, "Host:");
if (str_host == NULL)
printf("--) Host: Campo no definido\n");
else
imprimeHastaEscape(str_host);
char *str_user_agent = strstr(buffer, "User-Agent:");
if (str_user_agent == NULL)
printf("--) User-Agent: Campo no definido\n");
else
imprimeHastaEscape(str_user_agent);
char *str_connection = strstr(buffer, "Connection:");
if (str_connection == NULL)
printf("--) Connection: Campo no definido\n");
else
imprimeHastaEscape(str_connection);
char *str_accept_language = strstr(buffer, "Accept-Language:");
if (str_accept_language == NULL)
printf("--) Accept-Language: Campo no definido\n");
else
imprimeHastaEscape(str_accept_language);
}
/**************************************************************
*@brief Funcion que es llamada cada vez que se detecta un paquete en la captura,
* Utilizando las estructuras auxiliares definidas en pcap.h
* Obtiene la longitud del paquete, muestra la IP fuente y destino,
* En caso de que el paquete cumpla el protocolo TCP, investiga la cabecera TCP,
* Para mostrar los datos requeridos de Request y Response HTTP
***************************************************************/
void procesarPaquete(u_char *args, const struct pcap_pkthdr *header, const u_char *paquete){
// Informacion de cabecera del paquete
indice++;
//printf("Longitud del mensaje en total: %d\n", header->len);
// Procesando el header de IP
const struct ip_header *ip;
int size_ip;
ip = (struct ip_header*) (paquete + TAM_ETHERNET);
size_ip = IP_HL(ip)*4;
if(size_ip < 20){
//printf("x) La longitud del header IP es invalida: %u bytes\n\n", size_ip);
return;
}
// Remitente y destinatario (direcciones IP)
//printf("(SRC: %p) %s -> (DEST: %p) %s \n", (void*)&(*ip).ip_src, inet_ntoa(ip->ip_src), (void*)&(*ip).ip_dst, inet_ntoa(ip->ip_dst));
// En caso de que el paquete sea del protocolo TCP se continua con el proceso
if(ip->ip_p == IPPROTO_TCP){
const struct sniff_tcp *tcp; /* TCP header */
int size_tcp;
const char *payload; /* Packet payload */
int size_payload;
char* nombre;
nombre = "TCP";
/* Parsing del header tcp */
tcp = (struct sniff_tcp*) (paquete + TAM_ETHERNET + size_ip);
size_tcp = TH_OFF(tcp)*4;
if(size_tcp < 20){
//printf("x) La longitud del header TCP es invalida: %u bytes\n\n", size_tcp);
return;
}
// Proceso del segmento de informacion del header TPC
payload = (u_char *) (paquete + TAM_ETHERNET + size_ip + size_tcp);
size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp);
// Mostrando informacion de cabecera TCP
if (size_payload > 0){
// Si comienza con algunas de las siguientes palabras reservadas, el paquete nos interesa
if( empiezaCon(payload, "HTTP") || empiezaCon(payload, "POST") || empiezaCon(payload, "GET") ||
empiezaCon(payload, "HEAD") || empiezaCon(payload, "OPTIONS") || empiezaCon(payload, "PUT") ||
empiezaCon(payload, "DELETE") || empiezaCon(payload, "TRACE") || empiezaCon(payload, "CONNECT")){
printf("(--------------- INDICE PAQUETE OBTENIDO (%d) ----------------)\n", indice);
printf("--) Protocolo: %x (%s)\n", ip->ip_p, nombre);
printf(" Puerto remitente: %d\n", ntohs(tcp->th_sport));
printf(" Puerto destino: %d\n", ntohs(tcp->th_dport));
if (empiezaCon(payload, "HTTP")){
//Muestra la informacion de un response
printf("-) RESPONSE\n");
imprimeResponse(payload);
}else{
//Muestra la informacion de un request
printf("-) REQUEST\n");
imprimeRequest(payload);
}
printf("X--------------- PAQUETE PROCESADO ---------------X\n\n");
}
}
/* Fin del parsing del header TCP */
}else{
// Protocolo no relacionado a HTTP
//printf("--) El paquete (%d) utilza un protocolo no relacionado con HTTP\n", indice);
//printf(" Numero identificador del protocolo: %d\n\n", ip->ip_p);
return;
}
}
/**************************************************************
*@brief Dada una captura y el nombre del dispositivo,
* itera sobre cada paquete dentro de la captura indefinidamente,
* o hasta que ocurra un error
***************************************************************/
int leerPaquetes(pcap_t* captura, char* dev){
//Iteramos sobre los paquetes utilizando la funcion auxiliar para cada uno (-1 = Lectura indefinida)
pcap_loop(captura, -1, procesarPaquete, NULL);
// Seria bueno ver como llegar a este close, debido a que pcap_loop es "infinito"
pcap_close(captura);
return EXIT_SUCCESS;
}
/**************************************************************
*@brief Rutina que muestra los dispositivos de red que pueden ser escuchados,
y permite elegir uno para realizar una captura y ser analizado
***************************************************************/
int capturaDispositivo(){
// Obtenemos la lista de interfaces de red que pueden abrirse
char ebuf[PCAP_ERRBUF_SIZE];
pcap_if_t* deviceList;
if (pcap_findalldevs(&deviceList,ebuf) == -1){
printf("Al obtener listado de dispositivos. ERROR: %s\n", ebuf);
return EXIT_FAILURE;
}else{
printf("--) Lista de interfaces de red:\n");
}
// Iteramos la lista de dispositivos disponibles
int i = 0;
while(deviceList->next != NULL ) {
printf(" %d.- %s\n", i+1, (deviceList->name));
deviceList = deviceList->next;
i++;
}
//Obtenemos la interfaz de red tecleada por el usuario
printf("--) Teclea el NOMBRE de la interfaz de red con la que deseas interactuar:\n");
char dev[256];
fgets(dev, sizeof dev, stdin);
char *pos;
if ((pos = strchr(dev, '\n')) != NULL)
*pos = '\0';
//Si la cadena con el nombre del dispositivo de red no es válida, no continuamos
if(dev == NULL){
printf("x) Nombre de dispositivo no valido. ERROR\n");
return EXIT_FAILURE;
}
//Imprimimos el dispositivo de red a capturar
printf("--) Capturaremos del dispositivo: %s\n\n", dev);
//Abrimos la interfaz de red
pcap_t *captura;
captura = pcap_open_live(dev, BUFSIZ, 1, 1000, ebuf);
//Si la captura realizada no fue exitosa salimos del programa
if(captura == NULL) {
printf("x) En captura. ERROR: %s\n", ebuf);
return EXIT_FAILURE;
}
// Aplicamos filtro para unicamente actuar en paquetes http
bpf_u_int32 mask; //Netmask del sniffer
bpf_u_int32 net; //IP del sniffer
if (pcap_lookupnet(dev, &net, &mask, ebuf) == -1){
printf("Error al configurar filtro: %s\n", ebuf);
return EXIT_FAILURE;
}
struct bpf_program fp;
// En el puerto 80 ocurre el intercambio de paquetes http a traves de tcp
//pcap_compile(captura, &fp, "tcp port 80", 0, net);
pcap_compile(captura, &fp, "tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)", 0, net);
pcap_setfilter(captura, &fp);
// Con la captura lista procedemos a leer sus paquetes
leerPaquetes(captura, dev);
}
/**************************************************************
*@brief Rutina en la que se recibe el nombre del archivo,
y realiza la captura con los datos que contiene
***************************************************************/
int leeCaptura(){
char ebuf[PCAP_ERRBUF_SIZE];
printf("--) Teclea el NOMBRE del archivo que contiene la captura a analizar.\n");
char dev[256];
fgets(dev, sizeof dev, stdin);
char *pos;
if ((pos = strchr(dev, '\n')) != NULL)
*pos = '\0';
//Si la cadena con el nombre del archivo no es válida, termina la ejecucion
if(dev == NULL){
printf("x) Nombre de dispositivo no valido. ERROR\n");
return EXIT_FAILURE;
}
//Imprimimos el nombre del archivo con la captura
printf("--) Leyendo archivo: %s\n\n", dev);
//Abrimos la captura a partir del archivo
pcap_t *captura;
captura = pcap_open_offline(dev, ebuf);
//Si la captura realizada no fue exitosa salimos del programa
if(captura == NULL) {
printf("x) En captura. ERROR: %s\n", ebuf);
return EXIT_FAILURE;
}
// Con la captura lista procedemos a leer sus paquetes
leerPaquetes(captura, dev);
}
/**************************************************************
*@brief Rutina que limpia la entrada standard
***************************************************************/
int clean_stdin(){
while (getchar()!='\n');
return 1;
}
/**************************************************************
*@brief Funcion principal del programa, permite eligir el
* modo de trabajo
***************************************************************/
int main () {
// Capturamos respuesta y continuamos a hacer lo correspondiente a la opcion elegida
int entrada;
char c;
printf("--) Elige el modo en el que se trabajara, tecleando el NUMERO:\n");
do{
printf(" (1) Escucha indefinida\n (2) Lectura de captura\n");
}while(((scanf("%d%c", &entrada, &c)!=2 || c!='\n') && clean_stdin()) || entrada<1 || entrada>2);
if (entrada == 1){
// Escucha indefinida
capturaDispositivo();
}else{
// Lectura de captura
leeCaptura();
}
return 0;
}