Descarga la aplicación para disfrutar aún más
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; }
Compartir