Logo Studenta

UAM _Universidad Autónoma de Madrid_ _ Ingeniería de telecomunicaciones _ asignatura_ Arquitectura de redes _ Recursos p

Esta es una vista previa del archivo. Inicie sesión para ver el archivo original

funciones.c
/*
Ejemplos de uso de funciones necesarias para el desarrollo de la practica 2
Al incluirlas de cualquier forma en la entrega deben ser completadas sus cabeceras,
y los convenientes controles de error anadidos
 
 Compila: gcc -Wall -o funciones funciones.c
 Autor: Jose Luis Garcia Dorado
 (c) 2017 EPS-UAM 
**/
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//AÑADIR CABECERA! (obligatorio para usar esta funcion), al menos:
//Nombre funcion, fecha
//Funcionalidad, para que sirve/que hace
//Entrada
//Salida
//Comentarios
int funcion2(char *name1,char *name2){
	char *token;
	int i, j = 0;
	token = strtok(name1, ".");
	while(token != NULL){
		i = strlen(token);
		name2[j]=i;
		j = j+1;
		strcpy(name2+j, token);
		j = j+i;
		token = strtok(NULL, ".");
	}
	name2[j]=0;
	return j+1;
}
//AÑADIR CABECERA! (obligatorio para usar esta funcion), al menos:
//Nombre funcion, fecha
//Funcionalidad, para que sirve/que hace
//Entrada
//Salida
//Comentarios
int funcion1(char *name2,char *name1){
	int i=0, j=0,n=0;
	while (name2[j] != 0){
		n = name2[j];
		j++;
		strncpy(name1+i,name2+j,n);
		j+=n;
		i+=n;
		strncpy(name1+i,".",1);
		i++;
	}
	name1[i-1]=0;
	return i;
}
// NOTA: 
// La funcion descrita a continuacion (main) unicamente cumple 
// el proposito de validacion de las funciones proporcionadas. 
// Para la integracion con el codigo final a entregar 
// debera ser comentada!
int main(int argc, char *argv[])
{
char ejemplo1[]={3,'w','w','w',3,'u','a','m',2,'e','s',0};
char retorno[2083]={0};
char retorno2[2083]={0};
printf("Entrada: %s__\n",ejemplo1);
funcion1(ejemplo1,retorno);
printf("Salida1: %s__\n",retorno);
funcion2(retorno,retorno2);
printf("Salida2: %s__\n",retorno2);
return 1;
}
makefile
CC=gcc
CFLAGS=-c 
LDFLAGS=
SOURCES= minslookup.c
OBJECTS=$(SOURCES:.c=.o)
EXECUTABLE=minslookup
#Ficheros TXT a incluir en la entrega
TXT=contenidos.txt
#Ficheros .h a incluir en la entrega
HEADERS=
all: $(SOURCES) $(EXECUTABLE)
	
$(EXECUTABLE): $(OBJECTS) 
	@echo -n compilando el Ejecutable \'$@\' ...
	@$(CC) $(LDFLAGS) $(OBJECTS) -o $@
	@echo [OK]
.c.o:
	@echo -n compilando Objeto \'$<\' ...
	@$(CC) $(CFLAGS) $< -o $@
	@echo [OK]
clean:
	@echo -n limpiando proyecto ...
	@rm -f $(wildcard *.o *.d core* *.P) $(EXECUTABLE)
	@echo [OK]
entrega:
	@echo Introduce tu grupo [3261,3262,3263]; \
	read g; \
	echo Introduce tu numero de pareja (dos digitos):; \
	read p;\
	echo creando zip de entrega "P1_"$$g"_P"$$p".zip"...;\
	zip "P1_"$$g"_P"$$p".zip" $(SOURCES) $(TXT) $(HEADERS) makefile	
	
	@echo [OK]
minslookup.c
// Ejemplo de sockets en Linux que mandan y reciben datagramaas UDP
#include <stdio.h>
#include <stdlib.h>
// Estas cabeceras son necesarias para los 'sockets'
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h> // Usado por 'timeval'
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> // Usado por 'close'
#include <stdint.h>
#include <string.h> // Usado por 'strlen'
#define DNS_SERVER "8.8.8.8"
#define DNS_PORT 53
#define MAX_datagrama_SIZE 6000
uint16_t id=0;
/****************************************************************************************
*Nombre: getNameDNS									*
*Descripcion: Esta funcion devuelve el nombre en formato DNS realizando			*
*	 reconstrucciones siguiendo los punteros DNS				*	
*Argumentos:										*
*	-data: puntero al lugar de la trama donde comienza el nombre que se desea 	*
*	 reconstruir								*
*	-datagramaStart: puntero al comienzo del mensaje DNS				*
*	-reconstructedName: cadena de caracteres donde se guardara el nombre 		*
*	 reconstruido. Debe ser una cadena con tamaño reservado								*
*Retorno: - La funcion devuelve el numero de caracteres que se han leido hasta encontrar	*
*	 el primer puntero DNS en caso de que los haya o hasta encontrar el final de 	*
*	 la cadena DNS																			* 
	-reconstructedName (como argumento)								*
*											*
****************************************************************************************/
int getNameDNS(uint8_t *data,uint8_t *datagramaStart,char *reconstructedName)
{
	uint8_t *aux=data;
	int j=0,k=0,flag=0;
	while(aux[0]!=0)
	{
		if(aux[0]==0xC0)
		{
			aux=datagramaStart+aux[1];
			if(flag==0)
			{
				flag=1;
				k++;
			}
		}
		else
		{
			reconstructedName[j]=((char)aux[0]);
			j++;
			aux++;
			if(flag==0)
			k++;
		}
	}
	return k+1;
}
/****************************************************************************************
*Nombre: domain2DNS									*
*Descripcion: Esta funcion convierte un nombre de dominio de la forma www.google.es	*
*	 al formato de cadena de DNS (3www6google2es0) OJO los numeros de la 	*
*	 cadena estan en valor decimal y no en ASCII				*
*Funcionamiento: A RELLENAR				*
*Argumentos:										*
*	-domain: nombre de dominio en formato estandar					*
*	-DNSname: nombre de dominio en formato DNS					*
*Retorno: la funcion devuelve la longitud de la cadena DNSname o -1 en caso de error	*
*											*
****************************************************************************************/
int domain2DNS(char *domain,char *DNSname)
{
	/*PRACTICA IMPLEMENTAR AQUI*/
	return 0;
}
/****************************************************************************************
*Nombre: DNS2domain									*
*Descripcion: Esta funcion convierte un nombre en formato DNS(3www6google2es0) a un 	*
*	 nombre de dominio estandar (www.google.es). OJO los numeros de la cadena	*
*	 estan en valor decimal y no en ASCII	 
*Funcionamiento: A RELLENAR				*
*Argumentos:										*
*	-DNSname: nombre de dominio en formato DNS					*										
*	-domain: nombre de dominio en formato estandar					*
*Retorno: la funcion devuelve la longitud de name o -1 en caso de error			*
*											*
****************************************************************************************/
int DNS2domain(char *DNSname,char *domain)
{
	/*PRACTICA IMPLEMENTAR AQUI*/
	return 0;
}
/****************************************************************************************
*Nombre: buildQuery									*
*Descripcion: Esta funcion construye una peticion DNS rellenando la variable datagrama	*
*Argumentos:										*
*	-datagrama: array de bytes donde se construira la peticion DNS			*
*	-namee: nombre de dominio a resolver (ej. www.uam.es)				*
*Retorno: la funcion devuelve un entero indicando la longitud de datos utiles contenidos*
*	 en la variable datagrama. En caso de error devuelve -1.				*
*											*
****************************************************************************************/
int buildQuery(uint8_t * datagrama, char *namee)
{
	uint16_t aux16=0;
	uint32_t size=0;
	uint16_t flags=0x0100; //banderas todos los campos a 0 para peticion menos bit RD a 1	
	uint16_t number_questions=1,number_answers=0,number_auth=0,number_additional=0;
	//Se copia el ID de peticion DNS. EL identificador se incrementa para cada peticion
	aux16=htons(id);
	id++;
	memcpy(datagrama+size,&aux16,sizeof(uint16_t));
	size+=sizeof(uint16_t);
	//se copian los flags
	aux16=htons(flags);
	memcpy(datagrama+size,&aux16,sizeof(uint16_t));
	size+=sizeof(uint16_t);
	//se copian el numero de preguntas a realizar (en nuestro caso 1)
	aux16=htons(number_questions);
	memcpy(datagrama+size,&aux16,sizeof(uint16_t));
	size+=sizeof(uint16_t);
	//se copian el numero de respuestas que se envian (en nuestro caso 0)
	aux16=htons(number_answers);
	memcpy(datagrama+size,&aux16,sizeof(uint16_t));
	size+=sizeof(uint16_t);
	//se copian el numero de respuestas de autoridades que se envian (en nuestro caso 0)
	aux16=htons(number_auth);
	memcpy(datagrama+size,&aux16,sizeof(uint16_t));
	size+=sizeof(uint16_t);
	//se copian el numero de respuestas de adicionales que se envian (en nuestro caso 0)
	aux16=htons(number_additional);
	memcpy(datagrama+size,&aux16,sizeof(uint16_t));
	size+=(1 * sizeof(uint16_t));
	
	/*PRACTICA IMPLEMENTAR AQUI*/
	/*Construir resto de la cabecera. Para ello:
		-Convertir la variable
namee a formato DNS y copiarla con memcpy
		-Copiar con memcpy el campo tipo rellenando con valor A (1)
		-Copiar con memcpy el campo clase rellenando con valor IN (1)
	*/
	
	return size;	
}
/****************************************************************************************
*Nombre: parseDNSResponse								*
*Descripcion: Esta funcion analiza una respuesta DNS almacenada en la variable datagrama	*
*	 La respuesta tendra la longitud indicada en la variable size		*
*Argumentos:										*
*	-datagrama: array de bytes donde se encuentra la respuesta DNS			*
*	-size: tamano en bytes de la respuesta DNS					*
*Retorno: La funcion devuelve 0 si todo es correcto y -1 en caso contrario		*
*											*
****************************************************************************************/
int parseResponse(uint8_t * datagrama, int size)
{
	uint16_t aux16=0;
	uint16_t flags=0;	
	uint16_t auxId=0;
	uint32_t sizeRead=0;
	uint16_t number_questions=0,number_answers=0,number_auth=0,number_additional=0;
	int i=0;
	//Se extrae el identificador de la peticion DNS
	memcpy(&aux16,datagrama+sizeRead,sizeof(uint16_t));
	auxId=ntohs(aux16);
	sizeRead+=sizeof(uint16_t);
	
	//Se debe comprobar que el identificador de la respuesta coincide con el de la pregunta (id-1)
	//Se extraen las flags
	memcpy(&aux16,datagrama+sizeRead,sizeof(uint16_t));
	flags=ntohs(aux16);
	sizeRead+=sizeof(uint16_t);
	
	//Se debe comprobar que el bit QR de las banderas esta a 1 y los bits Rcode a 0 (No error)
	//Se extrae el numero de preguntas que vienen incluidas en la respuesta DNS
	memcpy(&aux16,datagrama+sizeRead,sizeof(uint16_t));
	number_questions=ntohs(aux16);
	sizeRead+=sizeof(uint16_t);
	//Se extrae el numero de respuestas que vienen incluidas en la respuesta DNS
	memcpy(&aux16,datagrama+sizeRead,sizeof(uint16_t));
	number_answers=ntohs(aux16);
	sizeRead+=sizeof(uint16_t);
	//Se extrae el numero de respuestas autoritativas que vienen incluidas en la respuesta DNS
	memcpy(&aux16,datagrama+sizeRead,sizeof(uint16_t));
	number_auth=ntohs(aux16);
	sizeRead+=sizeof(uint16_t);
	//Se extrae el numero de respuestas adicionales que vienen incluidas en la respuesta DNS
	memcpy(&aux16,datagrama+sizeRead,1 * sizeof(uint16_t));
	number_additional=ntohs(aux16);
	sizeRead+=sizeof(uint16_t);
	for(i=0;i<number_questions;i++)
	{
		printf("Pregunta %d\n",i);
	/*PRACTICA IMPLEMENTAR AQUI*/
	/*Leer preguntas incluidas en la respuesta DNS. Para ello:
		-Leer y convertir a formato estandar el campo query name 
		-Leer el campo tipo
		-Leer el campo clase 
		-Comprobar que tipo y clase estan a 1
	*/
	}
	for(i=0;i<number_answers;i++)
	{
		printf("Respuesta %d\n",i);
	/*PRACTICA IMPLEMENTAR AQUI*/
	/*Leer respuestas incluidas en la respuesta DNS. Para ello:
		-Leer y convertir a formato estandar el campo name (ojo el campo name puede contener punteros DNS)
		-Leer el campo tipo para obtener si la repsuesta es una IP A(valor 1) o un nombre canonico CNAME(valor 5)
		-Leer el campo clase y comprobar que es IN(valor 1)
		-Leer el campo TTL (no sera usado en esta practica)
		-Leer el campo RdataLen. Este campo valdra 4 en caso de que la respuesta sea de tipo 1 o un numero
		variable en caso de que el tipo sea 5.
		-Leer e imprimir RDATA. En caso de respuestas de tipo 1, imprimir por pantalla la IP. En caso de peticiones de tipo 5
		imprimir por pantalla el nombre canonico. En caso de respuestas de tipo 5 el campo RDATA puede contener punteros DNS.
	*/
	}
	return 0;	
}
int main(int argc, char *argv[])
{
 struct sockaddr_in sock_addr, to_addr;
 socklen_t sockaddrlen = sizeof(struct sockaddr);
 int sock, c;
 struct timeval tv; // Usado para el 'timeout'
 fd_set rfds; // Usado por el 'select'
 uint8_t datagrama[MAX_datagrama_SIZE];
 int n=0;
 bzero(datagrama,5999);
 fprintf( stdout, "Programa ejemplo cliente DNS...\n");
 
	if(argc != 2)
	{
		printf("Numero de argumentos incorrectos\n");
		printf("Uso: ./minslookup direccion\n");
		exit(-1);
	}
	// Crear un socket para manejar datagramaas UDP
	sock = socket( AF_INET, SOCK_DGRAM, 0 );
	if(sock == -1)
	{
		perror("El socket no pudo ser creado!\n");
		exit(-1);
	} //endif
	// Activaremos una propiedad del socket que permitira que otros
	// sockets puedan reutilizar cualquier puerto al que nos enlacemos.
	// Esto permitira en protocolos como el TCP, poder ejecutar un
	// mismo programa varias veces seguidas y enlazarlo siempre al
	// mismo puerto. De lo contrario habria que esperar a que el puerto
	// quedase disponible (TIME_WAIT en el caso de TCP)
	c = 1; // Opción requerida por 'setsockopt'
	setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char *)&c, sizeof(c) );
	// Otro problema que hay que resolver es el bloqueo que se produce
	// en las funciones de envio y recepcion de informacion.
	// Si no se le indica nada al socket, todas las operaciones son 
	// bloqueantes. Esto afecta especialmente a la recepcion de datos,
	// puesto que hasta que no lleguen, la funcion no devolvera el
	// control. Para evitar bloqueos se puede usar la funcion 'select'
	// antes de recibir datos. Aqui solamente se van a preparar las
	// estructuras y el timeout que usara el 'select'.
	FD_ZERO( &rfds );
	// Activar el 'indicador de lectura' asociado al socket creado.
	// Cuando se reciban datos, se activara el 'indicador de lectura'.
	FD_SET( sock, &rfds);
	tv.tv_sec = 2; // Segundos del 'timeout'.
	tv.tv_usec = 0; // Microsegundos del 'timeout'.
	// Preparar una estructura con informacion sobre como vamos
	// a recibir los datagramaas UDP
	// En este ejemplo, enlazaremos el socket a la IP local a cualquier 
	// puerto UDP que esta libre. Esto lo conseguiremos inicializando
	// la estructura a cero mediante, por ejemplo, 'bzero'.
	// Si especificasemos un puerto mediante el comando:
	// sock_addr.sin_port = htons(port);
	// entonces forzariamos el enlace del socket al puerto numero 'port'
	bzero( (char *)&sock_addr, sizeof(sock_addr) );
	sock_addr.sin_family = AF_INET;
	// Enlazar el socket creado al puerto que hemos descrito usando
	// la estructura anterior. Este paso es obligatorio si se desea
	// recibir cualquier datagramaa.
	if( bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr) ) == -1 )
	{
		perror("El socket no pudo ser enlazado a un puerto libre !\n");
		exit(-1); // No se pudo enlazar el socket. Habria que reintentarlo...
	} //endif
	// Preparar una estructura donde indicaremos la direccion IP a donde
	// vamos a enviar los datagramaas y el puerto al que queremos que vayan
	// dirigidos.
	to_addr.sin_addr.s_addr = inet_addr(DNS_SERVER);// IP del DNS de Google
	to_addr.sin_port = htons(DNS_PORT);			// Puerto del DNS
	to_addr.sin_family = AF_INET;
	// Enviaremos un datagramaa a dicha direccion IP y puerto, con unos
	// datos de ejemplo.
	// En el caso de la practica de DNS, estos datos corresponderan
	// con una trama DNS que se explica en el guion de la practica.
 
	if((n=buildQuery(datagrama,argv[1]))<0)
	{
		 perror("Error construyendo peticion DNS\n");
		exit(-1);
	}
 	
	c = sendto( sock,datagrama,n, 0, (struct sockaddr *)&to_addr, sockaddrlen );
	if(c == -1)
	{
		perror("No se pudo enviar el datagramaa por el socket !\n");
		close( sock );
		exit(-1);
	} //endif
	// NOTA IMPORTANTE 1:
	// Cuando se envia un datagramaa UDP a traves de un socket enlazado,
	// el puerto UDP de destino es el indicado por la estructura usada en
	// funcion 'sendto'. Pero el puerto UDP de origen es el que se uso
	// en la funcion 'bind' (que en este ejemplo es el primero libre que haya)
	// Cuando un servidor UDP recibe un 'request' en un puerto dedicado y
	// bien conocido (como es el 53 para el DNS), la respuesta la envia
	// al puerto de origen que le viene en el datagramaa recibido.
	// NOTA IMPORTANTE 2:
	// Como ya se ha indicado arriba, la recepcion de datos no es bloqueante.
	// Tras un determinado 'timeout' la funcion 'select' devolvera un error 
	// si no se han recibido datos por el socket. En caso contrario, la
// funcion 'select' indicara que ya hay datos disponibles y entonces
	// podremos llamar a 'recvfrom' sin que se produzca un bloqueo.
	// Por lo tanto, esperaremos la respuesta del servidor por nuestro 
	// puerto de origen (fuere cual fuere) que hemos enlazado al principio.
	c = select( FD_SETSIZE, &rfds, (fd_set *)NULL, (fd_set *)NULL, &tv );
	if(c == -1)
	{
		perror("El 'select' tuvo un error !\n");
		close(sock);
		exit(1);
	} //endif
	if(c == 0)
	{
		// El 'select' ha indicado que el tiempo indicado en 'tv' ha acabado
		fprintf( stdout, "Timeout expirado... Respuesta no recibida !\n");
	}
	else
	{
		// El 'select' ha indicado que hay datos pendientes de ser recibidos
		c = recv( sock, datagrama, MAX_datagrama_SIZE, 0 );
		// Nota: La funcion 'recvfrom' hace lo mismo, solo que ademas tambien
		// rellena una estructura indicando desde que direccion IP nos
		// han enviado el datagramaa, y cual es el puerto de origen.
		if(c == -1)
		{
			perror("Error al tratar de recibir un datagramaa UDP !\n");
			close( sock );
			exit(-1);
		} //endif
		// Mostrar los datos recibidos. Si hemos mandado basura al DNS
		// nos contestara con una trama indicando que hay un error en la
		// estructura del 'query' que le enviamos.
		fprintf(stdout, "Datos obtenidos:");
		if(parseResponse(datagrama,c)<0)
		{
			 perror("Error al procesar un datagramaa UDP !\n");
			 if (sock) close( sock );
			 exit(-1);
		}
	} //endif
	fprintf(stdout, "\n");
 
 // Cerrar el socket y volver
	close( sock );
 
 return 0;
}

Continuar navegando