dazibao/src/node.c
2020-04-01 18:43:53 +02:00

383 lines
11 KiB
C

// This is the main file of the Dazibao project. It represents the node, and
// handles all of the main logic, including the network connexions.
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include "tlv.h"
#include "node.h"
/* ---- Fonctions utilitaires ---- */
// Get list length
int len_list(list *l) {
int len = 0;
list *tmp = l;
while(tmp != NULL) {
tmp = tmp->next;
len++;
}
return len;
}
// Get a random neighbour
neighbour_peer *get_random_neighbour() {
// Get a random number
time_t t;
srand((unsigned) time(&t));
int n = rand() % len_list(neighbour_list);
// Get nth neighbour
list *tmp = neighbour_list;
for(int i=0; i<n; i++) {
tmp = tmp->next;
}
return (neighbour_peer*) tmp->data;
}
/* ---- Fin fonctions utilitaires ---- */
int send_tlv(struct tlv tlv_to_send, int tlv_type, struct sockaddr_in6 * dest_list[], int dest_list_size, int sock_num){
debug_print("Building packet to send a TLV.");
// We first need to build the packet,
char packet_buff[1024];
struct packet pack;
pack.magic = 95;
pack.version = 1;
if(sizeof(tlv_to_send) > 1020){
debug_print("Unable to send TLV, the size is bigger than 1020 bits.");
return -1;
} else {
pack.length = sizeof(tlv_to_send);
strcpy(pack.body, (char) tlv_to_send);
}
// Casting the struct to a buffer.
packet_buff = (char *) pack;
debug_print("Packet has been built.");
// Vectorized buffer
struct iovec vec_buff = { .iov_len = sizeof(packet_buff), .iov_base = packet_buff };
int error_while_sending = 0;
// For every dest
for (size_t i = 0; i < dest_list_size; i++) {
// Creating the struct to send out with sendmsg
struct msghdr packet_tlv_send_out = {
.msg_name = &dest_list[i],
.msg_namelen = sizeof(dest_list[i]),
.msg_iov = &vec_buff,
.msg_iovlen = 1 // We have only one iovec buffer. But if we had 2, we would write 2.
};
response_code = sendmsg((int) sock_num, &packet_tlv_send_out, 0);
if (response_code < 0) {
debug_print("Unable to send out the packet to peer %i", i);
error_while_sending = 1;
continue;
} else if (response_code < sizeof(packet_tlv_send_out)) {
debug_print("Sent out only part of the packet.");
error_while_sending = 1;
continue;
} else {
debug_print("Send out packet to peer %i", i);
}
}
if (error_while_sending == 1) {
debug_print("Error occured while sending out a packet.");
return -1;
} else {
return 0;
}
}
// We need to make sure the TLV announces a length that will no go onto
// another tlv, as we might end up reading bullshit.
int validate_tlv(char *data, int pos, short packet_len){
char type = data[pos];
// Nothing to do in this case
if(type == 0)
return 0;
// Check that we can read a length
if(pos + 1 >= packet_len)
return -1;
unsigned char tlv_len = data[pos+1];
// Check that the tlv does not exceed the packet length
if(pos + length >= packet_len)
return -1;
// Returns the type of the tlv or -1 if something went wrong
switch(type) {
case 1:
return 1;
case 2:
if(tlv_len != LEN_NEIGHBOUR_REQ) return -1;
return 2;
case 3:
if(tlv_len != LEN_NEIGHBOUR) return -1;
return 3;
case 4:
if(tlv_len != LEN_NETWORK_HASH) return -1;
return 4;
case 5:
if(tlv_len != LEN_NETWORK_STATE_REQ) return -1;
return 5;
case 6:
if(tlv_len != LEN_NODE_HASH) return -1;
return 6;
case 7:
if(tlv_len != LEN_NODE_STATE_REQ) return -1;
return 7;
case 8:
if(tlv_len < MIN_LEN_NODE_STATE || tlv_len > MAX_LEN_NODE_STATE) return -1;
return 8;
case 9:
return 9;
default:
return -1;
}
}
// For every packet recivied,
// then we make sure it's conform
// We then extract the data from it to make it easy to work with
int check_header(char * req[], int buffer_size, struct packet * packet_to_return){
packet * packet_to_return = (packet*) req;
// We need to check a few things ;
// The first byte must be worth 95,
if (packet_to_return->magic != 95) {
perror(">> The magic number of the packet is no good.");
return -1;
}
// The second byte must be worth 1,
if (packet_to_return->version != 1) {
perror(">> The version number of the packet is no good.");
return -1;
}
if (packet_to_return.length + 4 > buffer_size ) {
perror(">> The packet length is bigger than the UDP datagram, which is not possible with the current laws of physics.");
return -1;
}
return 0;
}
// If the sender is not in the neighbourhood, and we have 15 neighbours, we
// ignore the packet. Otherwise, we add him to the neighbourhood, marked as
// temporary.
int update_neighbours(){
return 0;
};
// We then look at the differents TLVs in the packet.
void work_with_tlvs(char *data, short packet_len, struct sockaddr_in6 sender){
int pos = 0;
unsigned char tlv_len;
tlv tmp_tlv;
while(pos < packet_len) {
switch(validate_tlv(data, pos, packet_len)) {
case 0:
// We received a padding tlv so it is ignored
pos += 1;
break;
case 1:
// We received a padding tlv so it is ignored
tlv_len = data[pos+1];
pos += tlv_len + 2;
break;
case 2:
// We received a neighbour request so a random neighbor tlv has to be sent
tlv_len = data[pos+1];
pos += tlv_len + 2;
// Send a neighbour tlv
neighbour_peer *random = get_random_neighbour();
build_neighbour(&tmp_tlv, random->ip, random->port);
// NOT FINISHED - What packet is it added to?
add_tlv(packet, &tmp_tlv, 3);
break;
case 3:
// We received a neighbour tlv so a tlv network hash is sent to that address
neighbour* cur_tlv = ((neighbour*) data) + pos;
struct in6_addr ip = cur_tlv->ip;
short port = cur_tlv->port;
tlv_len = data[pos+1];
pos += tlv_len + 2;
// Build network hash
unsigned char hash[16];
hash_network(neighbour_list, hash);
build_network_hash(&tmp_tlv, hash);
// NOT FINISHED - What packet is it added to?
add_tlv(packet, &tmp_tlv, 4);
break;
case 4:
// We reveived a network hash tlv so
tlv_len = data[pos+1];
pos += tlv_len +2;
// NOT FINISHED - Where is network_hash?
build_neighbour(&tmp_tlv, network_hash);
// NOT FINISHED - What packet is it added to?
add_tlv(packet, &tmp_tlv, 4);
break;
case 5:
// We received a network state request tlv so a series of tlv node hash have to be sent for each data known
pos += 2;
// NOT FINISHED - for each known data
list *tmp_list = data_list;
pub_data *tmp_data;
while(tmp_list != NULL) {
tmp_data = (pub_data*) tmp_list->data;
build_node_hash(&tmp_tlv, tmp_data->id, tmp_data->seqno);
}
break;
case 6:
// We received a node hash tlv
break;
case 7:
// We received a node state request tlv
break;
case 8:
// We received a node state tlv
break;
case 9:
// We received a warning tlv so it's message is printed
break;
default:
return ;
}
}
}
// We listen forever for new paquets;
void listen_for_packets(){
// Create new socket for UDP
int s = socket(AF_INET6, SOCK_DGRAM, 0);
if(s < 0) {
perror(">> Error, cannot create socket.");
perror(">> Exiting...");
exit(1);
}
struct sockaddr_in6 server;
memset(&server, 0, sizeof(server));
server.sin6_family = AF_INET6;
server.sin6_port = htons(LISTEN_PORT);
int rc = bind(s, (struct sockaddr*)&server, sizeof(server));
if(rc < 0) {
perror(">> Error, cannot bind socket to choosen port.");
perror(">> Exiting...");
exit(1);
}
// A paquet has at most a length of 1024 bytes
char req[1024];
struct sockaddr_in6 sender;
struct iovec io = { .iov_len = 1024, .iov_base = req };
struct msghdr msg_to_receive = {
.msg_name = &sender,
.msg_namelen = sizeof(sender),
.msg_iov = &io,
.msg_iovlen = 1
};
while(1){
memset(req, '\0', 1024);
rc = recvmsg(s, &msg_to_receive, 0);
if(rc < 0) {
perror(">> Error while receiving a new datagram.");
perror(">> Ignoring, continuing...");
continue;
}
printf(">> New paquet received :\n");
printf("%s\n", req);
// TODO : Here, we need to fork.
// We verify the received packet is well formated,
// and we return it in the struct designed to work with it.
struct packet * formated_rec_datagram;
if(check_header(&req, 1024, formated_rec_datagram) < 0){
perror(">> Error while checking the header, aborting this packet, by choice, and conviction.");
continue;
}
// TODO : Add the neighbour check here.
// struct tlv_list received_tlvs;
// if (validate_tlvs(formated_rec_datagram) < 0)
int nbr_success_tlv = work_with_tlvs(formated_rec_datagram, &req, sender);
if (nbr_success_tlv < 0){
perror(">> Error while treating the TLVs of the packet.");
printf(">> Managed to deal with %i TLVs\n", -nbr_success_tlv );
} else {
printf(">> Done working with the TLVs of the packet, listenin for new packets.\n");
}
}
}
int main(int argc, const char *argv[]) {
int cont = 1;
while(cont){
// We create the neighbourhood table
neighbour_peer neighbour_list[NEIGHBOUR_MAX];
// We create the message table
// We create our own message.
// Listen for incoming packets
listen_for_packets();
// This is in it's own fork.
time_t delay = time(NULL) + 20;
while(! (delay < time(NULL))) {
// Theses functions are there for general book-keeping,and run in there own
// thread, being run every 20 seconds.
// Every 20 sec, if we have less than 5 neighbours, we ask for more peers
// by sending out a TLV neighbour Request at a random peer.
t_ask_for_more_peers();
// Every 20 sec, we also check for a peer that didn't emit a new message for
// the past 70 sec, if he's temporary, we delete him from the neighbourhood.
t_update_neighbours();
// We send out a TLV Network hash to get an ideal of the network state.
t_get_network_state();
}
}
return 0;
}