#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SERVER_BACKLOG 100 #define INFO_WAIT_TIME 2 #define TCP_WAIT_TIME 3 #define FIRST_ALIVE_TIMEOUT 3 #define ALIVE_TIMEOUT 2 #define M 3 /*--------------TIPUS DE PAQUETS-------------*/ /* Paquets de la fase de registre */ unsigned char REG_REQ = 0x00; unsigned char REG_INFO = 0x01; unsigned char REG_ACK = 0x02; unsigned char INFO_ACK = 0x03; unsigned char REG_NACK = 0x04; unsigned char INFO_NACK = 0x05; unsigned char REG_REJ = 0x06; /* Paquets per a la comunicació periòdica */ unsigned char ALIVE = 0x10; unsigned char ALIVE_REJ = 0x11; /* Paquets per a la transferència de dades amb el servidor */ unsigned char SEND_DATA = 0x20; unsigned char SET_DATA = 0x21; unsigned char GET_DATA = 0x22; unsigned char DATA_ACK = 0x23; unsigned char DATA_NACK = 0x24; unsigned char DATA_REJ = 0x25; /*---------------ESTATS----------------*/ unsigned char DISCONNECTED = 0xa0; unsigned char NOT_REGISTERED = 0xa1; unsigned char WAIT_ACK_REG = 0xa2; unsigned char WAIT_INFO = 0xa3; unsigned char WAIT_ACK_INFO = 0xa4; unsigned char REGISTERED = 0xa5; unsigned char SEND_ALIVE = 0xa6; /*-------------STRUCTS-------------*/ typedef struct client_info { unsigned char state; char id[13]; char randNum[9]; char elems[50]; char ip[16]; unsigned short tcpPort; time_t lastAlive; } client_info; typedef struct clients_db { client_info *clients; int length; } clients_db; typedef struct udp_pdu { unsigned char pack; char id[13]; char randNum[9]; char data[61]; } udp_pdu; typedef struct tcp_pdu { unsigned char pack; char id[13]; char randNum[9]; char elem[8]; char value[16]; char data[80]; } tcp_pdu; typedef struct config { char *id; int udpPort; int tcpPort; } config; typedef struct reg_thread_args { udp_pdu pdu; int socket; struct sockaddr_in clientAddress; } reg_thread_args; int generateRandNum(int min, int max) { srand(time(0)); return rand() % (max + 1 - min) + min; } /*--------------------------------------------*/ /*---------------DEBUG UTILS------------------*/ /*--------------------------------------------*/ int DEBUG_ON = 0; void debugPrint(const char *message) { if (DEBUG_ON) { printf("%s => %s\n", __TIME__, message); fflush(stdout); } } void check(int result, const char *message) { if (result < 0) { perror(message); exit(EXIT_FAILURE); } } /*--------------------------------------------*/ /*---------------DATABASE UTILS---------------*/ /*--------------------------------------------*/ int isAuthorized(clients_db *db, const char *id) { for (int i = 0; i < db->length; i++) if (strcmp(id, db->clients[i].id) == 0) return i; return -1; } void disconnectClient(client_info *client) { if (client->state != DISCONNECTED) { char debugMessage[60] = {'\0'}; sprintf(debugMessage, "Client %s pasa a l'estat DISCONNECTED", client->id); debugPrint(debugMessage); } client->state = DISCONNECTED; memset(client->elems, '\0', sizeof(client->elems)); memset(client->ip, '\0', sizeof(client->ip)); memset(client->randNum, '\0', sizeof(client->randNum)); client->tcpPort = -1; client->lastAlive = -1; } int shareClientsInfo(clients_db *db) { int shmid; if ((shmid = shmget(IPC_PRIVATE, db->length * sizeof(client_info), IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR)) == -1) return -1; client_info *tmp; if ((tmp = shmat(shmid, (void *)0, 0)) == (void *)-1) return -1; for (int i = 0; i < db->length; i++) tmp[i] = db->clients[i]; free(db->clients); db->clients = tmp; return 0; } int hasElem(const char *elem, client_info *client) { char cpy[50] = {'\0'}; strcpy(cpy, client->elems); char *token = strtok(cpy, ";"); while (token != NULL) { if (strcmp(elem, token) == 0) return 1; token = strtok(NULL, ";"); } return 0; } /*--------------------------------------------*/ /*---------------SOCKETS UTILS----------------*/ /*--------------------------------------------*/ int sendUdp(int sock, unsigned char pack, char id[13], char randNum[9], char data[61], struct sockaddr_in address) { udp_pdu pdu; pdu.pack = pack; strcpy(pdu.id, id); strcpy(pdu.randNum, randNum); strcpy(pdu.data, data); return sendto(sock, &pdu, sizeof(udp_pdu), 0, (struct sockaddr *)&address, sizeof(struct sockaddr_in)); } int sendTcp(int sock, unsigned char pack, char id[13], char randNum[9], char elem[8], char value[16], char info[80]) { tcp_pdu pdu; pdu.pack = pack; strcpy(pdu.id, id); strcpy(pdu.randNum, randNum); strcpy(pdu.elem, elem); strcpy(pdu.value, value); strcpy(pdu.data, info); return send(sock, &pdu, sizeof(tcp_pdu), 0); } int selectIn(int sock, fd_set *inputs, int timeout) { struct timeval t; t.tv_sec = timeout; t.tv_usec = 0; FD_ZERO(inputs); FD_SET(sock, inputs); return select(sock + 1, inputs, NULL, NULL, &t); } int bindTo(int sock, uint16_t port) { struct sockaddr_in address; address.sin_family = AF_INET; address.sin_port = htons(port); address.sin_addr.s_addr = INADDR_ANY; return bind(sock, (struct sockaddr *)&address, sizeof(struct sockaddr_in)); } int connectTo(int sock, const char *ip, unsigned short port) { struct sockaddr_in address; address.sin_family = AF_INET; address.sin_port = htons(port); address.sin_addr.s_addr = inet_addr(ip); return connect(sock, (struct sockaddr *)&address, sizeof(struct sockaddr_in)); } int getPort(int fd) { struct sockaddr_in address; socklen_t len = sizeof(struct sockaddr_in); int port = 0; memset(&address, 0, sizeof(struct sockaddr_in)); if (getsockname(fd, (struct sockaddr *)&address, &len) == -1) return -1; port = ntohs(address.sin_port); return port; } /*----------------------------------------*/ /*-------------READING UTILS--------------*/ /*----------------------------------------*/ char *getLine(FILE *fp) { size_t len = 3; char *line = (char *)malloc(len * sizeof(char)); int c = fgetc(fp); int i = 0; while (c != '\n' && c != EOF) { line[i] = c; i++; if (i == len) { len++; line = (char *)realloc(line, len * sizeof(char)); } c = fgetc(fp); } if (i == 0) { free(line); return NULL; } line[i] = '\0'; return line; } char *trim(char *str) { char *end; /*left trim*/ while (isspace((unsigned char)*str)) str++; if (*str == 0) /*Es tot espais*/ return str; /*Right trim*/ end = str + strlen(str) - 1; while (end > str && (isspace((unsigned char)*end) || (unsigned char)*end == '\n')) end--; end[1] = '\0'; return str; } char *getCfgLineInfo(FILE *fp) { char *line = getLine(fp); if (line == NULL) return NULL; char *tmp = strrchr(line, '='); tmp++; char *info = trim(tmp); if (strlen(info) <= 0) return NULL; return info; } int readConfig(config *cfg, const char *filename) { char *attrs[3]; FILE *fp; if ((fp = fopen(filename, "r")) == NULL) return -1; for (int i = 0; i < 3; i++) { if ((attrs[i] = getCfgLineInfo(fp)) == NULL) { fclose(fp); return -1; } } cfg->id = attrs[0]; cfg->udpPort = atoi(attrs[1]); cfg->tcpPort = atoi(attrs[2]); fclose(fp); return 0; } int readDb(clients_db *db, const char *filename) { FILE *fp; if ((fp = fopen(filename, "r")) == NULL) return -1; client_info *clients = (client_info *)malloc(sizeof(client_info)); char *line; int len = 1, i = 0; while ((line = getLine(fp)) != NULL) { if (i == len) { len++; clients = (client_info *)realloc(clients, len * sizeof(client_info)); } client_info client; memset(&client, 0, sizeof(client_info)); client.state = DISCONNECTED; strcpy(client.id, line); memset(client.elems, '\0', sizeof(client.elems)); memset(client.ip, '\0', sizeof(client.ip)); memset(client.randNum, '\0', sizeof(client.randNum)); client.tcpPort = -1; client.lastAlive = -1; clients[i] = client; i++; } if (i == 0) { fclose(fp); free(clients); return -1; } fclose(fp); db->clients = clients; db->length = len; return 0; }