#include #include #include #include #include #include #include #include #include #include #include #include #include "helpers.h" // Private Functions char *get_ip_from_sockaddr(address_t addr) { // Beware: The inet_ntoa() function returns a string in a statically allocated buffer, which // subsequent calls will overwrite! return inet_ntoa(addr.sin_addr); // TODO: maybe change to this instead: // #define MAKE_FN_NAME(x) void Callback_ ## x (void) // #define FUNCTION_NAME(signal) MAKE_FN_NAME(signal) } // APIs node_id_t extract_id_from_ip(const ipv4_t ip) { assert(ip); const char separator[2] = "."; node_id_t id = 0; char *rest, *token, *ip_cp; ip_cp = malloc(strlen(ip) * sizeof(char)); strcpy(ip_cp, ip); rest = ip_cp; token = strtok_r(rest, separator, &rest); if (!token || atoi(token) != 10) { return 0; } token = strtok_r(rest, separator, &rest); if (!token || atoi(token) != 0) { return 0; } token = strtok_r(rest, separator, &rest); if (!token) { return 0; } id = atoi(token) * 100; token = strtok_r(rest, separator, &rest); if (!token) { return 0; } id += atoi(token); free(ip_cp); return id; } void set_timer_and_handler(void (*handler)(int), long int wakeup_time) { assert(handler); logI("Setting up new wakeup timer."); struct itimerval timer; struct sigaction signal_action; // Installs handler as the signal handler for SIGALRM // Needs to be set every time! memset(&signal_action, 0, sizeof(signal_action)); signal_action.sa_handler = handler; if (sigaction(SIGALRM, &signal_action, NULL)) { logE("Couldn't install function handler for SIGALRM signals."); logI("Exiting..."); exit(EXIT_FAILURE); } // Sets a timer to deliver a SIGALRM signal in wakeup_time seconds timer.it_interval.tv_usec = 0; timer.it_interval.tv_sec = 0; timer.it_value.tv_usec = 0; timer.it_value.tv_sec = wakeup_time; if (setitimer(ITIMER_REAL, &timer, NULL) == -1) { logE("Couldn't set timer."); logI("Exiting..."); exit(EXIT_FAILURE); } } uint16_t create_message(char *new_message, uint16_t max_message_length, node_handle_t *neighbors, uint8_t num_neighbors, node_id_t own_id) { assert(new_message && neighbors); logI("Creating new message."); node_handle_t random_node = neighbors[rand() % (num_neighbors)]; node_id_t peer_id = node_get_id(random_node); if (!peer_id) { logE("Couldn't extract peer's ID."); logI("Exiting..."); exit(EXIT_FAILURE); } snprintf(new_message, max_message_length, "%04d_%04d_%ld_%s", own_id, peer_id, time(NULL), "It's amazing... It's fantastic!"); return strlen(new_message); } /* * Function based on this example: * https://www.gnu.org/software/libc/manual/html_node/Inet-Example.html#Inet-Example */ int create_socket_and_listen(port_t port, uint8_t backlog_size) { logI("Creating the socket for input connections."); int in_sock; address_t own_name; // Creates the socket in_sock = socket(PF_INET, SOCK_STREAM, 0); if (in_sock < 0) { logE("Couldn't create the socket."); logI("Exiting..."); exit(EXIT_FAILURE); } // Gives the socket a name own_name.sin_family = AF_INET; own_name.sin_port = htons(port); own_name.sin_addr.s_addr = htonl(INADDR_ANY); // Binds own address structure to socket if (bind(in_sock, (struct sockaddr *) &own_name, sizeof(own_name)) < 0) { logE("Couldn't bind the address structure to the socket."); logI("Exiting..."); exit(EXIT_FAILURE); } if (listen(in_sock, backlog_size) < 0) { logE("Couldn't listen for connections on the socket."); logI("Exiting..."); exit(EXIT_FAILURE); } return in_sock; } void send_message(node_handle_t peer, message_handle_t message) { assert(peer && message); logI("Sending message.") int out_sock; address_t peer_address = node_get_addr(peer); // Creates the socket out_sock = socket(PF_INET, SOCK_STREAM, 0); if (out_sock < 0) { logE("Couldn't create the socket."); logI("Exiting..."); exit(EXIT_FAILURE); } // Connects to the peer if (connect(out_sock, (struct sockaddr *) &(peer_address), sizeof(node_get_addr(peer)))) { printf("Couldn't connect to peer : %s.\n", get_ip_from_sockaddr(node_get_addr(peer))); node_add_timestamp(peer, time(NULL), false); } else { // Sends data to the peer int ignored; write_to_peer(out_sock, message_get(message, &ignored)); node_add_timestamp(peer, time(NULL), true); message_add_sent_to(message, node_get_id(peer)); } close(out_sock); } node_id_t accept_connection(int sock, address_t *peer_name, fd_set *active_fd_set, int *listens_on) { assert(peer_name && active_fd_set && listens_on); logI("Accepting new connection."); size_t peer_name_size = sizeof((*peer_name)); int comm_socket = accept(sock, (struct sockaddr *) peer_name, &peer_name_size); if (comm_socket < 0) { logE("Couldn't accept the connection."); logI("Exiting..."); // TODO maye be remove this? exit(EXIT_FAILURE); } *(listens_on) = comm_socket; char *node_addr = get_ip_from_sockaddr(*peer_name); fprintf(stderr, "Connected to host %s, port %hd.\n", node_addr, ntohs((*peer_name).sin_port)); FD_SET(comm_socket, active_fd_set); return extract_id_from_ip(node_addr); } uint16_t checkAddNode(node_handle_t **neighbors, uint16_t num_neighbors , address_t peer_name, node_id_t node_id) { assert(neighbors); logI("Adding new node."); bool node_exists = false; for (int i = 0; i < num_neighbors; ++i) { if (node_get_id((*neighbors)[i]) == node_id) { continue; } } if (!node_exists) { node_handle_t *r_neighbors = realloc((*neighbors), (num_neighbors + 1) * sizeof(node_handle_t)); if (!r_neighbors) { free(r_neighbors); logE("Unable to reallocate memory for neighbor IP."); logI("Exiting..."); exit(EXIT_FAILURE); } (*neighbors) = r_neighbors; (*neighbors)[num_neighbors] = node_init(peer_name, node_id); return num_neighbors + 1; } return num_neighbors; } void write_to_peer(int file_desc, const char *message) { assert(message); logI("Writing to peer."); int num_bytes = write(file_desc, message, strlen(message) + 1); if (num_bytes < 0) { logE("Couldn't write to peer."); logI("Exiting..."); // TODO maye be remove this? exit(EXIT_FAILURE); } } int read_from_peer(int file_des, uint16_t max_line, cbuf_handle_t *message_buffer, node_handle_t **neighbors, uint16_t num_neighbors) { logI("Reading from peer."); char buffer[max_line]; int num_bytes; num_bytes = read(file_des, buffer, sizeof(buffer)); if (num_bytes < 0) { logE("Couldn't read from peer."); logI("Exiting..."); // TODO maye be remove this? exit(EXIT_FAILURE); } else if (num_bytes == 0) { // End-of-file return -1; } else { fprintf(stderr, "Got message: `%s'\n", buffer); node_id_t received_from = -1; for (int i = 0; i < num_neighbors; ++i) { if (node_get_comm_socket((*neighbors)[i])) { received_from = node_get_id((*neighbors)[i]); node_set_comm_socket((*neighbors)[i], -1); break; } } message_handle_t new_message = message_init(buffer, num_bytes, received_from); circ_buf_put(*(message_buffer), &new_message); return 0; } } uint16_t fread_neighbors(char *AEM_file, node_handle_t **neighbors, port_t port) { assert(AEM_file && neighbors); logI("Reading peers from file."); uint16_t num_neighbors = 0; // Reads the ARP file checking for connected devices FILE *aems_file = fopen(AEM_file, "r"); if (!aems_file) { logE("Neighbors file: Failed to open file containing the AEMs."); logI("Exiting..."); exit(EXIT_FAILURE); } // Extracts IP addresses found in the file char ip_addr[AEM_BUFFER_LEN]; char **neighbors_ips = NULL; while (1 == fscanf(aems_file, AEM_LINE_FORMAT, ip_addr)) { ++num_neighbors; char **r_neighbors_ips = realloc(neighbors_ips, num_neighbors * sizeof(char *)); if (!r_neighbors_ips) { free(r_neighbors_ips); logE("Unable to reallocate memory for neighbor IP."); logI("Exiting..."); exit(EXIT_FAILURE); } neighbors_ips = r_neighbors_ips; neighbors_ips[num_neighbors - 1] = (char *) malloc(AEM_BUFFER_LEN * sizeof(char)); strcpy(neighbors_ips[num_neighbors - 1], ip_addr); } // Allocates memory for the new neighbors structs (*neighbors) = (node_handle_t *) malloc(num_neighbors * sizeof(node_handle_t)); if (!neighbors_ips) { logE("Unable to allocate memory for nodes."); logI("Exiting..."); exit(EXIT_FAILURE); } // Creates the neighbors structs for (uint8_t i = 0; i < num_neighbors; ++i) { address_t peer_name; init_sockaddr(&peer_name, build_ip_from_id(neighbors_ips[i]), port); (*neighbors)[i] = node_init(peer_name, atoi(neighbors_ips[i])); } fclose(aems_file); return num_neighbors; } /* * Function based on this example: * https://www.gnu.org/software/libc/manual/html_node/Inet-Example.html#Inet-Example */ void init_sockaddr(address_t *peer_name, const ipv4_t hostname, port_t port) { struct hostent *hostinfo; peer_name->sin_family = AF_INET; peer_name->sin_port = htons(port); hostinfo = gethostbyname(hostname); if (hostinfo == NULL) { fprintf(stderr, "Unknown host %s.\n", hostname); exit(EXIT_FAILURE); } peer_name->sin_addr = *(struct in_addr *) hostinfo->h_addr_list[0]; } char *build_ip_from_id(char *id) { static char ip[11]; node_id_t id_int = atoi(id); snprintf(ip, 11, "10.0.%2d.%2d", id_int / 100, id_int % 100); return ip; } void _zlog(LOG_LEVEL level, char *message, char *file, int line) { switch(level) { case DEBUG: printf(CYAN_BOLD "debug: %s:%d - %s\n" COLOR_RESET, file, line, message); break; case INFO: printf(BLUE_BOLD "info: %s:%d - %s\n" COLOR_RESET, file, line, message); break; case WARN: printf(YELLOW_BOLD "warn: %s:%d - %s\n" COLOR_RESET, file, line, message); break; case ERROR: printf(RED_BOLD "error: %s:%d - %s\n" COLOR_RESET, file, line, message); break; default: printf("%s:%d - %s\n", file, line, message); break; } }