You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
398 lines
9.8 KiB
398 lines
9.8 KiB
#include <unistd.h>
|
|
#include <ifaddrs.h>
|
|
#include <arpa/inet.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/select.h>
|
|
#include <sys/time.h>
|
|
#include <netdb.h>
|
|
#include <time.h>
|
|
#include <assert.h>
|
|
|
|
#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;
|
|
}
|
|
}
|
|
|