#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); 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)) { perror("Couldn't install function handler for SIGALRM signals."); 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) { perror("Couldn't set timer."); 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); node_handle_t random_node = neighbors[rand() % (num_neighbors)]; node_id_t peer_id = node_get_id(random_node); if (!peer_id) { perror("Couldn't extract peer's ID."); 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) { int in_sock; address_t own_name; // Creates the socket in_sock = socket(PF_INET, SOCK_STREAM, 0); if (in_sock < 0) { perror("Couldn't create the socket."); 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) { perror("Couldn't bind the address structure to the socket."); exit(EXIT_FAILURE); } if (listen(in_sock, backlog_size) < 0) { perror("Couldn't listen for connections on the socket."); exit(EXIT_FAILURE); } return in_sock; } void send_message(node_handle_t peer, message_handle_t message) { assert(peer && 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) { perror("Couldn't create the socket."); 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); size_t peer_name_size = sizeof((*peer_name)); int comm_socket = accept(sock, (struct sockaddr *) peer_name, &peer_name_size); if (comm_socket < 0) { perror("Couldn't accept the connection."); // 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); 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); perror("Unable to reallocate memory for neighbor IP."); 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); int num_bytes = write(file_desc, message, strlen(message) + 1); if (num_bytes < 0) { perror("Couldn't write to peer."); // 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) { char buffer[max_line]; int num_bytes; num_bytes = read(file_des, buffer, sizeof(buffer)); if (num_bytes < 0) { perror("Couldn't read from peer."); // 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); uint16_t num_neighbors = 0; // Reads the ARP file checking for connected devices FILE *aems_file = fopen(AEM_file, "r"); if (!aems_file) { perror("Neighbors file: Failed to open file containing the AEMs."); exit(EXIT_FAILURE); } // Extracts IP addresses found in the file char ip_addr[AEM_BUFFER_LEN]; char **neighbors_ips; 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); perror("Unable to reallocate memory for neighbor IP."); 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) { perror("Unable to allocate memory for nodes."); exit(EXIT_FAILURE); } // Creates the neighbors structs for (uint8_t i = 0; i < num_neighbors; ++i) { address_t peer_name; init_sockaddr(&peer_name, neighbors_ips[i], port); (*neighbors)[num_neighbors++] = node_init(peer_name, extract_id_from_ip(get_ip_from_sockaddr(peer_name))); } 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) { assert(peer_name && hostname); 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]; }