/**************************************************************************/ /* */ /* 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 o #include "meu.h" */ /* (si les funcions externes es cridessin entre elles, faria falta fer */ /* un #include "MIp1v4-mi.h") */ #include #include #include #include #include #include #include #include #include #include #include "MIp2-mi.h" #include /* 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 l’escolta de peticions remotes de conversa a través d’un nou */ /* socket TCP en el #port “portTCPloc” i una @IP local qualsevol (és a */ /* dir, crea un socket “servidor” o en estat d’escolta – listen –). */ /* Retorna -1 si hi ha error; l’identificador del socket d’escolta 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 d’escolta de MI d’identificador “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 s’omple “NickRem*” amb el nickname remot. El procés */ /* local és qui inicia aquest intercanvi (és a dir, primer s’envia 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; l’identificador 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 d’escolta de MI d’identificador “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 s’omple “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 s’envia 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; l’identificador 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 d’identificador “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 d’identificador */ /* “SckConvMI” (un socket “connectat”) la línia “Linia” escrita per */ /* l’usuari 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 d’identificador */ /* “SckConvMI” (un socket “connectat”) el nickname "Nick" escrit per */ /* l’usuari 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 d’identificador “SckConvMI” */ /* (un socket “connectat”) una línia escrita per l’usuari remot, amb la */ /* qual omple “Linia”, o bé detecta l’acabament de la conversa per part */ /* de l’usuari 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 l’usuari 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 */ /* d’identificador “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 l’escolta de peticions remotes de conversa que arriben a través */ /* del socket d’escolta de MI d’identificador “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; l’identificador 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 d’escolta – 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; l’identificador 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” d’identificador “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 l’estat “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” d’identificador “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 l’estat “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 s’ha 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; l’identificador 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” d’identificador “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” d’identificador “Sck” una */ /* seqüència de bytes que prové del socket remot amb qui està connectat, */ /* i l’escriu 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); } /* S’allibera (s’esborra) el socket TCP d’identificador “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 d’identificador “Sck”, troba l’adreça d’aquest */ /* 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” d’identificador “Sck”, troba l’adreç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 l’identificador d’aquest 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