lumi-practica-xarxes-udg/P2P/MIp2-mi.c

575 lines
24 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**************************************************************************/
/* */
/* P1 - MI amb sockets TCP/IP - Part I */
/* Fitxer mi.c que implementa la capa d'aplicació de MI, sobre la capa de */
/* transport TCP (fent crides a la interfície de la capa TCP -sockets-). */
/* */
/* Autors: Marc Cané Salamià, Enric Rodríguez Galán */
/* */
/**************************************************************************/
/* Inclusió de llibreries, p.e. #include <sys/types.h> o #include "meu.h" */
/* (si les funcions externes es cridessin entre elles, faria falta fer */
/* un #include "MIp1v4-mi.h") */
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include "MIp2-mi.h"
#include <ifaddrs.h>
/* Definició de constants, p.e., #define MAX_LINIA 150 */
#define TECLAT 0
#define MAX_FD 2
#define MAX_MESSAGE_SIZE 303
#define NOM_INTERFICIE "lo" //Aquest valor s'ha de canviar si es vol fer funcionar la funcio MI_ObtenirInfoServidor()
#define IP_LENGTH 16
/* Declaració de funcions internes que es fan servir en aquest fitxer */
/* (les seves definicions es troben més avall) per així fer-les conegudes */
/* des d'aqui fins al final de fitxer. */
int TCP_CreaSockClient(const char *IPloc, int portTCPloc);
int TCP_CreaSockServidor(const char *IPloc, int portTCPloc);
int TCP_DemanaConnexio(int Sck, const char *IPrem, int portTCPrem);
int TCP_AcceptaConnexio(int Sck, char *IPrem, int *portTCPrem);
int TCP_Envia(int Sck, const char *SeqBytes, int LongSeqBytes);
int TCP_Rep(int Sck, char *SeqBytes, int LongSeqBytes);
int TCP_TancaSock(int Sck);
int TCP_TrobaAdrSockLoc(int Sck, char *IPloc, int *portTCPloc);
int TCP_TrobaAdrSockRem(int Sck, char *IPrem, int *portTCPrem);
int HaArribatAlgunaCosa(const int *LlistaSck, int LongLlistaSck);
void MostraError(const char *text);
/* Definicio de funcions EXTERNES, és a dir, d'aquelles que en altres */
/* fitxers externs es faran servir. */
/* En termes de capes de l'aplicació, aquest conjunt de funcions externes */
/* formen la interfície de la capa MI. */
/* Inicia lescolta de peticions remotes de conversa a través dun nou */
/* socket TCP en el #port “portTCPloc” i una @IP local qualsevol (és a */
/* dir, crea un socket “servidor” o en estat descolta listen ). */
/* Retorna -1 si hi ha error; lidentificador del socket descolta de MI */
/* creat si tot va bé. */
int MI_IniciaEscPetiRemConv(int portTCPloc)
{
return TCP_CreaSockServidor("0.0.0.0", portTCPloc);
}
/* Escolta indefinidament fins que arriba una petició local de conversa */
/* a través del teclat o bé una petició remota de conversa a través del */
/* socket descolta de MI didentificador “SckEscMI” (un socket */
/* “servidor”). */
/* Retorna -1 si hi ha error; 0 si arriba una petició local; SckEscMI si */
/* arriba una petició remota. */
int MI_HaArribatPetiConv(int SckEscMI)
{
int FDs[] = {TECLAT, SckEscMI};
return HaArribatAlgunaCosa(FDs, MAX_FD);
}
/* Crea una conversa iniciada per una petició local que arriba a través */
/* del teclat: crea un socket TCP “client” (en un #port i @IP local */
/* qualsevol), a través del qual fa una petició de conversa a un procés */
/* remot, el qual les escolta a través del socket TCP ("servidor") d'@IP */
/* “IPrem” i #port “portTCPrem” (és a dir, crea un socket “connectat” o */
/* en estat establert established ). Aquest socket serà el que es farà */
/* servir durant la conversa. */
/* Omple “IPloc*” i “portTCPloc*” amb, respectivament, l@IP i el #port */
/* TCP del socket del procés local. */
/* El nickname local “NicLoc” i el nickname remot són intercanviats amb */
/* el procés remot, i somple “NickRem*” amb el nickname remot. El procés */
/* local és qui inicia aquest intercanvi (és a dir, primer senvia el */
/* nickname local i després es rep el nickname remot). */
/* "IPrem" i "IPloc*" són "strings" de C (vectors de chars imprimibles */
/* acabats en '\0') d'una longitud màxima de 16 chars (incloent '\0'). */
/* "NicLoc" i "NicRem*" són "strings" de C (vectors de chars imprimibles */
/* acabats en '\0') d'una longitud màxima de 300 chars (incloent '\0'). */
/* Retorna -1 si hi ha error; lidentificador del socket de conversa de */
/* MI creat si tot va bé. */
int MI_DemanaConv(const char *IPrem, int portTCPrem, char *IPloc, int *portTCPloc, const char *NicLoc, char *NicRem)
{
int sock;
sock = TCP_CreaSockClient("0.0.0.0", 0);
if (sock == -1) return -1;
if (TCP_DemanaConnexio(sock,IPrem, portTCPrem) == -1) return -1;
if(TCP_TrobaAdrSockLoc(sock, IPloc, portTCPloc) == -1) return -1;
if (MI_EnviaNickname(sock,NicLoc) == -1)
return -1;
char buff[300];
int long_nom = MI_RepLinia(sock,buff);
if (long_nom == -1 ) return -1;
//strncpy(NicRem, buff, long_nom);
strcpy(NicRem,buff);
//strcpy(NicRem,"setaci");
return sock;
}
/* Crea una conversa iniciada per una petició remota que arriba a través */
/* del socket descolta de MI didentificador “SckEscMI” (un socket */
/* “servidor”): accepta la petició i crea un socket (un socket */
/* “connectat” o en estat establert established ), que serà el que es */
/* farà servir durant la conversa. */
/* Omple “IPrem*”, “portTCPrem*”, “IPloc*” i “portTCPloc*” amb, */
/* respectivament, l@IP i el #port TCP del socket del procés remot i del */
/* socket del procés local. */
/* El nickname local “NicLoc” i el nickname remot són intercanviats amb */
/* el procés remot, i somple “NickRem*” amb el nickname remot. El procés */
/* remot és qui inicia aquest intercanvi (és a dir, primer es rep el */
/* nickname remot i després senvia el nickname local). */
/* "IPrem*" i "IPloc*" són "strings" de C (vectors de chars imprimibles */
/* acabats en '\0') d'una longitud màxima de 16 chars (incloent '\0'). */
/* "NicLoc" i "NicRem*" són "strings" de C (vectors de chars imprimibles */
/* acabats en '\0') d'una longitud màxima de 300 chars (incloent '\0'). */
/* Retorna -1 si hi ha error; lidentificador del socket de conversa */
/* de MI creat si tot va bé. */
int MI_AcceptaConv(int SckEscMI, char *IPrem, int *portTCPrem, char *IPloc, int *portTCPloc, const char *NicLoc, char *NicRem)
{
//if (TCP_TrobaAdrSockRem(SckEscMI, IPrem, portTCPrem) == -1) return -1;
int sock;
sock = TCP_AcceptaConnexio(SckEscMI,IPrem,portTCPrem);
if (sock == -1)
return -1;
if (TCP_TrobaAdrSockLoc(sock, IPloc, portTCPloc) == -1)
return -1;
if (TCP_TrobaAdrSockRem(sock, IPrem, portTCPrem) == -1)
return -1;
char buff[300];
int long_nom = MI_RepLinia(sock,buff);
if (long_nom == -1 ) return -1;
if (MI_EnviaNickname(sock,NicLoc) == -1)
return -1;
//strncpy(NicRem, buff, long_nom);
strcpy(NicRem,buff);
return sock;
}
/* Escolta indefinidament fins que arriba una línia local de conversa a */
/* través del teclat o bé una línia remota de conversa a través del */
/* socket de conversa de MI didentificador “SckConvMI” (un socket */
/* "connectat”). */
/* Retorna -1 si hi ha error; 0 si arriba una línia local; SckConvMI si */
/* arriba una línia remota. */
int MI_HaArribatLinia(int SckConvMI)
{
int FDs[] = {TECLAT, SckConvMI};
return HaArribatAlgunaCosa(FDs, MAX_FD);
}
/* Envia a través del socket de conversa de MI didentificador */
/* “SckConvMI” (un socket “connectat”) la línia “Linia” escrita per */
/* lusuari local. */
/* "Linia" és un "string" de C (vector de chars imprimibles acabat en */
/* '\0'), no conté el caràcter fi de línia ('\n') i té una longitud */
/* màxima de 300 chars (incloent '\0'). */
/* Retorna -1 si hi ha error; el nombre de caràcters n de la línia */
/* enviada (sense el \0) si tot va bé (0 <= n <= 299). */
int MI_EnviaLinia(int SckConvMI, const char *Linia)
{
char missatge[MAX_MESSAGE_SIZE+1], tipus = 'L';
int miss_len = strlen(Linia);
sprintf(missatge,"%c%.3d%s",tipus,miss_len,Linia);
return TCP_Envia(SckConvMI, missatge, miss_len+4);
}
/* Envia a través del socket de conversa de MI didentificador */
/* “SckConvMI” (un socket “connectat”) el nickname "Nick" escrit per */
/* lusuari local. */
/* "Nick" és un "string" de C (vector de chars imprimibles acabat en */
/* '\0'), no conté el caràcter fi de línia ('\n') i té una longitud */
/* màxima de 300 chars (incloent '\0'). */
/* Retorna -1 si hi ha error; el nombre de caràcters n del nickname */
/* enviat (sense el \0) si tot va bé (0 <= n <= 299). */
int MI_EnviaNickname(int SckConvMI, const char *Nick)
{
char nickname[MAX_MESSAGE_SIZE+1], tipus = 'N';
int miss_len = strlen(Nick);
sprintf(nickname,"%c%.3d%s",tipus,miss_len,Nick);
return TCP_Envia(SckConvMI, nickname, miss_len+4);
}
/* Rep a través del socket de conversa de MI didentificador “SckConvMI” */
/* (un socket “connectat”) una línia escrita per lusuari remot, amb la */
/* qual omple “Linia”, o bé detecta lacabament de la conversa per part */
/* de lusuari remot. */
/* "Linia*" és un "string" de C (vector de chars imprimibles acabat en */
/* '\0'), no conté el caràcter fi de línia ('\n') i té una longitud */
/* màxima de 300 chars (incloent '\0'). */
/* Retorna -1 si hi ha error; -2 si lusuari remot acaba la conversa; el */
/* nombre de caràcters n de la línia rebuda (sense el \0) si tot va bé */
/* (0 <= n <= 299). */
int MI_RepLinia(int SckConvMI, char *Linia)
{
int res;
char buffer[MAX_MESSAGE_SIZE];
res = TCP_Rep(SckConvMI, buffer, sizeof(buffer));
if (res > 0){
char len[3];
strncpy(len, buffer+1, sizeof(len));
int long_miss = atoi(len);
buffer[res]='\0';
strcpy(Linia, buffer+4);
res = long_miss;
}
else if (res == 0) res=-2;
return res;
}
/* Acaba la conversa associada al socket de conversa de MI */
/* didentificador “SckConvMI” (un socket “connectat”). */
/* Retorna -1 si hi ha error; un valor positiu qualsevol si tot va bé. */
int MI_AcabaConv(int SckConvMI)
{
return TCP_TancaSock(SckConvMI);
}
/* Acaba lescolta de peticions remotes de conversa que arriben a través */
/* del socket descolta de MI didentificador “SckEscMI” (un socket */
/* “servidor”). */
/* Retorna -1 si hi ha error; un valor positiu qualsevol si tot va bé. */
int MI_AcabaEscPetiRemConv(int SckEscMI)
{
return TCP_TancaSock(SckEscMI);
}
///******************************** FUNCIONS ADICIONALS DE LA CAPA MI ********************************///
/* Donat un socket descriptor sock, ens troba la IP i el port a on es */
/* troba. */
/* "IPloc" és un "string" de C (vector de chars imprimibles acabat en */
/* '\0') d'una longitud màxima de 16 chars (incloent '\0') */
/* Retorna -1 si algo ha anat malament, 0 si no hi ha hagut problemes */
int MI_ObtenirInfoServidor(int sock, char *IPloc, int *portTCPloc){
if (TCP_TrobaAdrSockLoc(sock, IPloc, portTCPloc) == -1) return -1; //Aixo ens dona el port pero no la IP
///**** Fragment de codi extret de: https://stackoverflow.com/questions/20800319/how-do-i-get-my-ip-address-in-c-on-linux ****///
struct ifaddrs *addrs,*tmp;
getifaddrs(&addrs); //funcio que ens retornara una llista encadenada d'interficies
tmp = addrs;
int trobat = 0;
while (tmp && !trobat) //Mentre ens quedin candidats i no el trobem
{
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) //Si la interficie es IPv4
{
struct sockaddr_in *pAddr = (struct sockaddr_in *)tmp->ifa_addr;
if (strcmp(tmp->ifa_name,NOM_INTERFICIE) == 0) { //Si hem trobat la nostre interficie
strcpy(IPloc,inet_ntoa(pAddr->sin_addr));
trobat = 1;
}
}
tmp = tmp->ifa_next;
}
freeifaddrs(addrs);
///***************************************************************************************************************************///
return 0;
}
/**************************************************************************/
/* */
/* P1 - MI amb sockets TCP/IP - Part I */
/* Versio numero 1.337 */
/* */
/* Autors: Marc Cané Salamià, Enric Rodríguez Galán */
/* */
/**************************************************************************/
/* Definicio de funcions */
/* Crea un socket TCP “client” a l@IP “IPloc” i #port TCP “portTCPloc” */
/* (si “IPloc” és “0.0.0.0” i/o “portTCPloc” és 0 es fa/farà una */
/* assignació implícita de l@IP i/o del #port TCP, respectivament). */
/* "IPloc" és un "string" de C (vector de chars imprimibles acabat en */
/* '\0') d'una longitud màxima de 16 chars (incloent '\0') */
/* Retorna -1 si hi ha error; lidentificador del socket creat si tot */
/* va bé. */
int TCP_CreaSockClient(const char *IPloc, int portTCPloc)
{
int sockdesc;
if ((sockdesc = socket(AF_INET,SOCK_STREAM,0)) == -1){
MostraError("Error creació socket client");
return -1;
}
else{
struct sockaddr_in adr;
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = inet_addr(IPloc);
adr.sin_port = htons(portTCPloc);
int i;
for(i=0;i<8;i++){adr.sin_zero[i]='0';}
if (bind(sockdesc,(struct sockaddr*)&adr,sizeof(adr))==-1){
MostraError("Error bind client");
return -1;
}
}
return sockdesc;
}
/* Crea un socket TCP “servidor” (o en estat descolta listen ) a */
/* l@IP “IPloc” i #port TCP “portTCPloc” (si “IPloc” és “0.0.0.0” i/o */
/* “portTCPloc” és 0 es fa una assignació implícita de l@IP i/o del */
/* #port TCP, respectivament). */
/* "IPloc" és un "string" de C (vector de chars imprimibles acabat en */
/* '\0') d'una longitud màxima de 16 chars (incloent '\0'). */
/* Retorna -1 si hi ha error; lidentificador del socket creat si tot */
/* va bé. */
int TCP_CreaSockServidor(const char *IPloc, int portTCPloc)
{
int sockdesc;
if ((sockdesc = socket(AF_INET,SOCK_STREAM,0)) == -1){
MostraError("Error creació socket servidor");
return -1;
}
struct sockaddr_in adr;
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = inet_addr(IPloc);
adr.sin_port = htons(portTCPloc);
int i;
for(i=0;i<8;i++){adr.sin_zero[i]='0';}
if (bind(sockdesc,(struct sockaddr*)&adr,sizeof(adr))==-1){
MostraError("Error bind servidor");
return -1;
}
if((listen(sockdesc,3))==-1){
MostraError("Error bind servidor");
return -1;
}
return sockdesc;
}
/* El socket TCP “client” didentificador “Sck” demana una connexió al */
/* socket TCP “servidor” d@IP “IPrem” i #port TCP “portTCPrem” (si tot */
/* va bé es diu que el socket “Sck” passa a lestat “connectat” o */
/* establert established ). */
/* "IPrem" és un "string" de C (vector de chars imprimibles acabat en */
/* '\0') d'una longitud màxima de 16 chars (incloent '\0'). */
/* Retorna -1 si hi ha error; un valor positiu qualsevol si tot va bé. */
int TCP_DemanaConnexio(int Sck, const char *IPrem, int portTCPrem)
{
struct sockaddr_in adr;
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = inet_addr(IPrem);
adr.sin_port = htons(portTCPrem);
int i;
for(i=0;i<8;i++){adr.sin_zero[i]='0';}
if((connect(Sck,(struct sockaddr*)&adr,sizeof(adr)))==-1){
char buff[100];
snprintf(buff, sizeof(buff), "Error: no s'ha pogut connectar amb %s", IPrem);
MostraError(buff);
return -1;
}
return 1;
}
/* El socket TCP “servidor” didentificador “Sck” accepta fer una */
/* connexió amb un socket TCP “client” remot, i crea un “nou” socket, */
/* que és el que es farà servir per enviar i rebre dades a través de la */
/* connexió (es diu que aquest nou socket es troba en lestat “connectat” */
/* o establert established ; el nou socket té la mateixa adreça que */
/* “Sck”). */
/* Omple “IPrem*” i “portTCPrem*” amb respectivament, l@IP i el #port */
/* TCP del socket remot amb qui sha establert la connexió. */
/* "IPrem*" és un "string" de C (vector de chars imprimibles acabat en */
/* '\0') d'una longitud màxima de 16 chars (incloent '\0'). */
/* Retorna -1 si hi ha error; lidentificador del socket connectat creat */
/* si tot va bé. */
int TCP_AcceptaConnexio(int Sck, char *IPrem, int *portTCPrem)
{
struct sockaddr_in adr;
int scon;
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = inet_addr(IPrem);
adr.sin_port = htons(*portTCPrem);
int i;
for(i=0;i<8;i++){adr.sin_zero[i]='0';}
size_t long_adr = sizeof(adr);
if((scon=accept(Sck,(struct sockaddr*)&adr, &long_adr))==-1)
{
char buff[100];
snprintf(buff, sizeof(buff), "Error: no s'ha pogut acceptar la connexio de %s", IPrem);
MostraError(buff);
return -1;
}
return scon;
}
/* Envia a través del socket TCP “connectat” didentificador “Sck” la */
/* seqüència de bytes escrita a “SeqBytes” (de longitud “LongSeqBytes” */
/* bytes) cap al socket TCP remot amb qui està connectat. */
/* "SeqBytes" és un vector de chars qualsevol (recordeu que en C, un */
/* char és un enter de 8 bits) d'una longitud >= LongSeqBytes bytes. */
/* Retorna -1 si hi ha error; el nombre de bytes enviats si tot va bé. */
int TCP_Envia(int Sck, const char *SeqBytes, int LongSeqBytes)
{
return write(Sck,SeqBytes,LongSeqBytes);
}
/* Rep a través del socket TCP “connectat” didentificador “Sck” una */
/* seqüència de bytes que prové del socket remot amb qui està connectat, */
/* i lescriu a “SeqBytes*” (que té una longitud de “LongSeqBytes” bytes),*/
/* o bé detecta que la connexió amb el socket remot ha estat tancada. */
/* "SeqBytes*" és un vector de chars qualsevol (recordeu que en C, un */
/* char és un enter de 8 bits) d'una longitud <= LongSeqBytes bytes. */
/* Retorna -1 si hi ha error; 0 si la connexió està tancada; el nombre de */
/* bytes rebuts si tot va bé. */
int TCP_Rep(int Sck, char *SeqBytes, int LongSeqBytes)
{
return read(Sck,SeqBytes,LongSeqBytes);
}
/* Sallibera (sesborra) el socket TCP didentificador “Sck”; si “Sck” */
/* està connectat es tanca la connexió TCP que té establerta. */
/* Retorna -1 si hi ha error; un valor positiu qualsevol si tot va bé. */
int TCP_TancaSock(int Sck)
{
return close(Sck);
}
/* Donat el socket TCP didentificador “Sck”, troba ladreça daquest */
/* socket, omplint “IPloc*” i “portTCPloc*” amb respectivament, la seva */
/* @IP i #port TCP. */
/* "IPloc*" és un "string" de C (vector de chars imprimibles acabat en */
/* '\0') d'una longitud màxima de 16 chars (incloent '\0'). */
/* Retorna -1 si hi ha error; un valor positiu qualsevol si tot va bé. */
int TCP_TrobaAdrSockLoc(int Sck, char *IPloc, int *portTCPloc)
{
struct sockaddr_in adr;
socklen_t long_adr = sizeof(adr);
int res;
if( (res = getsockname(Sck, (struct sockaddr*)&adr, &long_adr)) == -1){
MostraError("Error al trobar la direccio local");
return -1;
}
strcpy(IPloc,inet_ntoa(adr.sin_addr));
*portTCPloc = ntohs(adr.sin_port);
return res;
}
/* Donat el socket TCP “connectat” didentificador “Sck”, troba ladreça */
/* del socket remot amb qui està connectat, omplint “IPrem*” i */
/* “portTCPrem*” amb respectivament, la seva @IP i #port TCP. */
/* "IPrem*" és un "string" de C (vector de chars imprimibles acabat en */
/* '\0') d'una longitud màxima de 16 chars (incloent '\0'). */
/* Retorna -1 si hi ha error; un valor positiu qualsevol si tot va bé. */
int TCP_TrobaAdrSockRem(int Sck, char *IPrem, int *portTCPrem)
{
struct sockaddr_in adr;
socklen_t long_adr = sizeof(adr);
int res;
if ( (res = getpeername(Sck, (struct sockaddr*)&adr, &long_adr)) == -1) {
MostraError("Error al trobar la direccio remota");
return -1;
}
strcpy(IPrem,inet_ntoa(adr.sin_addr));
*portTCPrem = ntohs(adr.sin_port);
return res;
}
/* Examina simultàniament i sense límit de temps (una espera indefinida) */
/* els sockets (poden ser TCP, UDP i stdin) amb identificadors en la */
/* llista “LlistaSck” (de longitud “LongLlistaSck” sockets) per saber si */
/* hi ha arribat alguna cosa per ser llegida. */
/* "LlistaSck" és un vector d'enters d'una longitud >= LongLlistaSck */
/* Retorna -1 si hi ha error; si arriba alguna cosa per algun dels */
/* sockets, retorna lidentificador daquest socket. */
int HaArribatAlgunaCosa(const int *LlistaSck, int LongLlistaSck)
{
/*struct timeval timeout;
timeout.tv_sec = 30;
timeout.tv_usec = 0;*/ //estructura per definir un timeout de 30 segons al select
fd_set set;
FD_ZERO(&set);
int i = 0;
while (i<LongLlistaSck){
FD_SET(LlistaSck[i],&set);
i++;
}
int smax = LlistaSck[i-1]+1;
if (select(smax,&set,NULL,NULL,NULL) == -1){
MostraError("Error a la espera d'arribar alguna cosa");
return -1;
}
int ifd=0;
while( ifd < LongLlistaSck && !FD_ISSET(LlistaSck[ifd],&set) ) ifd++;
return LlistaSck[ifd];
}
/* Escriu un missatge de text al flux derror estàndard stderr, format */
/* pel text “Text”, un “:”, un espai en blanc, un text que descriu el */
/* darrer error produït en una crida de sockets, i un canvi de línia. */
void MostraError(const char *text)
{
fprintf(stderr, "%s: %s\n", text, strerror(errno));
}
/* Si ho creieu convenient, feu altres funcions... */