2019-07-06 17:26:09 +00:00
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <err.h>
|
2019-07-09 13:35:48 +00:00
|
|
|
#include <fcntl.h>
|
2019-07-06 17:26:09 +00:00
|
|
|
#include <inttypes.h>
|
2019-07-09 14:18:00 +00:00
|
|
|
#include <limits.h>
|
2019-07-06 17:26:09 +00:00
|
|
|
#include <linux/if_packet.h>
|
|
|
|
#include <net/ethernet.h>
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_arp.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/socket.h>
|
2019-07-09 13:35:48 +00:00
|
|
|
#include <sys/stat.h>
|
2019-07-06 17:26:09 +00:00
|
|
|
#include <sys/types.h>
|
2019-07-09 14:18:00 +00:00
|
|
|
#include <time.h>
|
2019-07-06 17:26:09 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2019-07-06 21:21:11 +00:00
|
|
|
#define EM_PROTOCOL_VERSION 0
|
2019-07-09 17:22:55 +00:00
|
|
|
#define EM_MESSAGE_MAX_LENGTH (1500 - 2 - 2 - 2)
|
2019-07-13 16:41:35 +00:00
|
|
|
#define EM_STATUS_BROADCAST_TIME (60 * 1000 + 1000 * random_byte() / 64)
|
2019-07-10 13:22:02 +00:00
|
|
|
#define EM_RETRANSMIT_TIME (1000 + random_byte() * 2)
|
|
|
|
#define EM_MAX_RETRANSMIT 5
|
2019-07-06 21:21:11 +00:00
|
|
|
|
|
|
|
#define EMT_SPEAK_VERSION 0
|
|
|
|
#define EMT_STATUS_REQUEST 1
|
|
|
|
#define EMT_STATUS 2
|
2019-07-07 16:37:51 +00:00
|
|
|
#define EMT_MSGID_REQUEST 3
|
|
|
|
#define EMT_MSGID 4
|
2019-07-09 16:16:23 +00:00
|
|
|
#define EMT_MESSAGE 5
|
|
|
|
#define EMT_ACK 6
|
2019-07-06 21:21:11 +00:00
|
|
|
|
|
|
|
#define EMS_AVAILABLE 0
|
|
|
|
#define EMS_UNAVAILABLE 1
|
2019-07-09 16:09:17 +00:00
|
|
|
#define EMS_OFFLINE 2
|
2019-07-06 21:21:11 +00:00
|
|
|
|
2019-07-06 17:26:09 +00:00
|
|
|
bool running = true;
|
|
|
|
|
|
|
|
int packet_socket;
|
2019-07-09 13:35:48 +00:00
|
|
|
int urandom;
|
2019-07-06 17:26:09 +00:00
|
|
|
|
|
|
|
unsigned char own_mac[6];
|
2019-07-06 21:21:11 +00:00
|
|
|
unsigned char broadcast_mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
|
|
|
|
|
|
|
unsigned char own_status = EMS_AVAILABLE;
|
2019-07-09 17:22:55 +00:00
|
|
|
unsigned char own_nick[256];
|
|
|
|
unsigned char own_nick_length = 0;
|
2019-07-10 12:12:11 +00:00
|
|
|
struct timespec next_status_broadcast;
|
2019-07-06 17:26:09 +00:00
|
|
|
|
2019-07-10 13:22:02 +00:00
|
|
|
enum message_send_states {IDLE, QUEUED, WAITING_MSGID, SENDING, WAITING_ACK};
|
2019-07-09 17:22:55 +00:00
|
|
|
enum message_send_states own_message_send_state = IDLE;
|
2019-07-07 16:37:51 +00:00
|
|
|
unsigned char own_message_destination_mac[6];
|
2019-07-09 17:22:55 +00:00
|
|
|
unsigned char own_message[EM_MESSAGE_MAX_LENGTH];
|
2019-07-07 16:37:51 +00:00
|
|
|
size_t own_message_length = 0;
|
2019-07-09 16:16:23 +00:00
|
|
|
uint16_t own_message_msgid = 0;
|
2019-07-10 13:22:02 +00:00
|
|
|
unsigned char retransmission_count = 0;
|
|
|
|
struct timespec next_retransmission;
|
2019-07-07 16:37:51 +00:00
|
|
|
|
|
|
|
struct msgid_cache_entry {
|
|
|
|
unsigned char other_mac[6];
|
|
|
|
bool know_send;
|
|
|
|
bool know_receive;
|
|
|
|
uint16_t next_send;
|
|
|
|
uint16_t next_receive;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct msgid_cache_entry msgid_cache[256];
|
|
|
|
ssize_t msgid_cache_fill = 0;
|
|
|
|
unsigned char next_slot = 0;
|
|
|
|
|
|
|
|
ssize_t msgid_cache_lookup(const unsigned char mac[6]) {
|
|
|
|
for (ssize_t i = 0; i < msgid_cache_fill; i++) {
|
|
|
|
if (memcmp(msgid_cache[i].other_mac, mac, 6) == 0) {
|
|
|
|
// Found it
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Did not find it
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t msgid_cache_add(const unsigned char mac[6]) {
|
|
|
|
ssize_t index = next_slot++;
|
|
|
|
|
2019-07-08 08:23:51 +00:00
|
|
|
// If we are adding into a new slot (instead of overwriting one),
|
|
|
|
// expand the fill pointer
|
2019-07-09 13:35:48 +00:00
|
|
|
//
|
|
|
|
// + 1 because msgid_cache_fill of N means that cache slots [0, N-1]
|
|
|
|
// are in use
|
|
|
|
if (msgid_cache_fill < index + 1) {
|
|
|
|
msgid_cache_fill = index + 1;
|
2019-07-08 08:23:51 +00:00
|
|
|
}
|
|
|
|
|
2019-07-07 16:37:51 +00:00
|
|
|
memcpy(msgid_cache[index].other_mac, mac, sizeof(msgid_cache[index].other_mac));
|
|
|
|
msgid_cache[index].know_send = false;
|
|
|
|
msgid_cache[index].know_receive = false;
|
|
|
|
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
2019-07-09 13:35:48 +00:00
|
|
|
unsigned char random_byte(void) {
|
|
|
|
unsigned char randomness;
|
|
|
|
if (read(urandom, &randomness, 1) != 1) {
|
|
|
|
err(1, "read");
|
|
|
|
}
|
|
|
|
return randomness;
|
|
|
|
}
|
|
|
|
|
2019-07-10 12:12:11 +00:00
|
|
|
struct timespec ms_in_future(intmax_t ms) {
|
|
|
|
struct timespec ts;
|
|
|
|
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) {
|
|
|
|
err(1, "clock_gettime");
|
|
|
|
}
|
|
|
|
|
|
|
|
// We can't really do anything to check for time_t overflow, because
|
|
|
|
// there are no macros to test for its size, so let's just hope it
|
|
|
|
// won't overflow
|
|
|
|
ts.tv_sec += ms / 1000;
|
|
|
|
#if LONG_MAX < 1999999999L
|
|
|
|
#error This code does time arithmetic, and requires a long big enough to hold almost 2 seconds worth of nanoseconds
|
|
|
|
#endif
|
|
|
|
ts.tv_nsec += (ms % 1000) * 1000 * 1000;
|
|
|
|
if (ts.tv_nsec >= 1000 * 1000 * 1000) {
|
2019-07-10 13:22:02 +00:00
|
|
|
ts.tv_sec += 1;
|
2019-07-10 12:12:11 +00:00
|
|
|
ts.tv_nsec -= 1000 * 1000 * 1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ts;
|
|
|
|
}
|
|
|
|
|
2019-07-10 17:25:26 +00:00
|
|
|
intmax_t saturating_add(intmax_t a, intmax_t b) {
|
|
|
|
if (a < 0 && b < 0) {
|
|
|
|
// a: [min, -1]
|
|
|
|
// b: [min, -1]
|
|
|
|
// min + min -> underflow
|
|
|
|
// min + -1 -> underflow
|
|
|
|
// -1 + -1 -> ok
|
|
|
|
// a + b < INTMAX_MIN || - a
|
|
|
|
// b < INTMAX_MIN - a
|
|
|
|
if (b < INTMAX_MIN - a) {
|
|
|
|
// Underflow
|
|
|
|
return INTMAX_MIN;
|
|
|
|
}
|
|
|
|
} else if (a < 0 && b >= 0) {
|
|
|
|
// a: [min, -1]
|
|
|
|
// b: [0, max]
|
|
|
|
// min + 0 -> ok
|
|
|
|
// min + max -> ok
|
|
|
|
// -1 + 0 -> ok
|
|
|
|
// -1 + max -> ok
|
|
|
|
} else if (a >= 0 && b < 0) {
|
|
|
|
// See above but swap a and b
|
|
|
|
} else if (a >= 0 && b >= 0) {
|
|
|
|
// a: [0, max]
|
|
|
|
// b: [0, max]
|
|
|
|
// 0 + 0 -> ok
|
|
|
|
// 0 + max -> ok
|
|
|
|
// max + max -> overflow
|
|
|
|
// a + b > INTMAX_MAX || -a
|
|
|
|
// b > INTMAX_MAX - a
|
|
|
|
if (b > INTMAX_MAX - a) {
|
|
|
|
// Overflow
|
|
|
|
return INTMAX_MAX;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return a + b;
|
|
|
|
}
|
|
|
|
|
|
|
|
intmax_t saturating_sub(intmax_t a, intmax_t b) {
|
|
|
|
// a - b = a + (-b)
|
|
|
|
// Only case where -b is not safe is when b < -INTMAX_MAX (because
|
|
|
|
// INTMAX_MIN can be smaller than -INTMAX_MAX, but INTMAX_MAX can't be
|
|
|
|
// larger than -INTMAX_MIN)
|
|
|
|
if (b < -INTMAX_MAX) {
|
|
|
|
// a - b || + c - c
|
|
|
|
// a - b + c - c
|
|
|
|
// a - (b - c) - c
|
|
|
|
// a + (c - b) - c
|
|
|
|
intmax_t c = saturating_sub(b, -INTMAX_MAX);
|
|
|
|
return saturating_sub(saturating_add(a, c - b), c);
|
|
|
|
} else {
|
2019-07-10 17:26:47 +00:00
|
|
|
return saturating_add(a, -b);
|
2019-07-10 17:25:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
intmax_t saturating_mul(intmax_t a, intmax_t b) {
|
|
|
|
// Doesn't give 100% right results when one parameter is INTMAX_MIN,
|
|
|
|
// but at least it won't ever overflow
|
2019-07-10 17:30:28 +00:00
|
|
|
if (a == 0 || b == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
2019-07-10 17:25:26 +00:00
|
|
|
if (a < 0) {
|
|
|
|
return saturating_sub(0, saturating_mul(saturating_sub(0, a), b));
|
|
|
|
}
|
|
|
|
if (b < 0) {
|
|
|
|
return saturating_sub(0, saturating_mul(a, saturating_sub(0, b)));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (INTMAX_MAX / a < b) {
|
|
|
|
// Overflow
|
|
|
|
return INTMAX_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
return a * b;
|
|
|
|
}
|
|
|
|
|
2019-07-10 12:12:11 +00:00
|
|
|
int wait_ms_until(struct timespec then) {
|
|
|
|
// This function basically returns the difference in ms between the
|
|
|
|
// current time and the given time, clamped to [0, INT_MAX]
|
|
|
|
//
|
|
|
|
// That format works for poll(2), which will immediately exit after
|
|
|
|
// checking status if timeout is 0, and otherwise wait the given
|
|
|
|
// number of ms
|
|
|
|
|
2019-07-09 14:18:00 +00:00
|
|
|
struct timespec now;
|
|
|
|
if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) {
|
|
|
|
err(1, "clock_gettime");
|
|
|
|
}
|
2019-07-10 12:12:11 +00:00
|
|
|
|
2019-07-10 17:25:26 +00:00
|
|
|
// Uses saturating arithmetic. I guess this might be wrong in some cases
|
|
|
|
// but can't be bothered to deal with it any other way, especially as a
|
|
|
|
// clamping the values to even something like [0, 1] would result in
|
|
|
|
// mostly correct functioning
|
|
|
|
intmax_t sec_diff = saturating_sub(then.tv_sec, now.tv_sec);
|
|
|
|
intmax_t ns_diff = saturating_sub(then.tv_nsec, now.tv_nsec);
|
|
|
|
intmax_t ms = saturating_add(saturating_mul(sec_diff, 1000), ns_diff / 1000 / 1000);
|
2019-07-10 12:12:11 +00:00
|
|
|
|
|
|
|
// Clamp
|
|
|
|
if (ms > INT_MAX) {
|
|
|
|
ms = INT_MAX;
|
|
|
|
} else if (ms < 0) {
|
|
|
|
ms = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (int)ms;
|
2019-07-09 14:18:00 +00:00
|
|
|
}
|
|
|
|
|
2019-07-06 17:26:09 +00:00
|
|
|
void drop_privileges(void) {
|
|
|
|
uid_t uid = getuid();
|
|
|
|
gid_t gid = getgid();
|
|
|
|
|
|
|
|
if (setresgid(gid, gid, gid) == -1) {
|
|
|
|
err(1, "setresgid");
|
|
|
|
}
|
|
|
|
if (setresuid(uid, uid, uid) == -1) {
|
|
|
|
err(1, "setresuid");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-06 21:21:11 +00:00
|
|
|
void send_frame(const unsigned char *frame, size_t frame_length) {
|
|
|
|
if (write(packet_socket, frame, frame_length) == -1) {
|
|
|
|
err(1, "write");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_headers(unsigned char frame[14], const unsigned char destination_mac[6], unsigned char packet_type) {
|
|
|
|
// Destination MAC
|
|
|
|
memcpy(&frame[0], destination_mac, 6);
|
|
|
|
|
|
|
|
// Source MAC
|
|
|
|
memcpy(&frame[6], own_mac, 6);
|
|
|
|
|
|
|
|
// EtherType
|
|
|
|
frame[12] = 0xda;
|
|
|
|
frame[13] = 0x7a;
|
|
|
|
|
|
|
|
// Ethermess version
|
|
|
|
frame[14] = EM_PROTOCOL_VERSION;
|
|
|
|
|
|
|
|
// Ethermess packet type
|
|
|
|
frame[15] = packet_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
void send_speak_version(const unsigned char destination[6]) {
|
|
|
|
unsigned char frame[14 + 2 + 1];
|
|
|
|
|
|
|
|
write_headers(frame, destination, EMT_SPEAK_VERSION);
|
|
|
|
|
|
|
|
// Version to speak
|
|
|
|
frame[16] = EM_PROTOCOL_VERSION;
|
|
|
|
|
|
|
|
send_frame(frame, sizeof(frame));
|
|
|
|
}
|
|
|
|
|
|
|
|
void send_status_request(const unsigned char destination[6]) {
|
|
|
|
unsigned char frame[14 + 2];
|
|
|
|
|
|
|
|
write_headers(frame, destination, EMT_STATUS_REQUEST);
|
|
|
|
|
|
|
|
send_frame(frame, sizeof(frame));
|
|
|
|
}
|
|
|
|
|
|
|
|
void send_status(const unsigned char destination[6]) {
|
|
|
|
unsigned char frame[14 + 2 + 1 + 1 + own_nick_length];
|
|
|
|
|
|
|
|
write_headers(frame, destination, EMT_STATUS);
|
|
|
|
|
|
|
|
// Status
|
|
|
|
frame[16] = own_status;
|
|
|
|
|
|
|
|
// Length of nick
|
|
|
|
frame[17] = own_nick_length;
|
|
|
|
|
|
|
|
// Nick
|
|
|
|
memcpy(&frame[18], own_nick, own_nick_length);
|
|
|
|
|
|
|
|
send_frame(frame, sizeof(frame));
|
|
|
|
}
|
|
|
|
|
2019-07-07 16:37:51 +00:00
|
|
|
void send_msgid_request(const unsigned char destination[6]) {
|
|
|
|
unsigned char frame[14 + 2];
|
|
|
|
|
|
|
|
write_headers(frame, destination, EMT_MSGID_REQUEST);
|
|
|
|
|
|
|
|
send_frame(frame, sizeof(frame));
|
|
|
|
}
|
|
|
|
|
|
|
|
void send_msgid(const unsigned char destination[6]) {
|
|
|
|
unsigned char frame[14 + 2 + 2];
|
|
|
|
|
|
|
|
write_headers(frame, destination, EMT_MSGID);
|
|
|
|
|
|
|
|
// Look up destination in the ID cache
|
|
|
|
ssize_t cache_index = msgid_cache_lookup(destination);
|
|
|
|
if (cache_index == -1) {
|
|
|
|
// Not in the cache
|
|
|
|
// Create a new entry
|
|
|
|
cache_index = msgid_cache_add(destination);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!msgid_cache[cache_index].know_receive) {
|
|
|
|
// We don't have receive ID stored in the cache
|
2019-07-09 13:35:48 +00:00
|
|
|
// In that case, start from a random index
|
|
|
|
msgid_cache[cache_index].next_receive = (random_byte() << 8) | random_byte();
|
2019-07-07 16:37:51 +00:00
|
|
|
msgid_cache[cache_index].know_receive = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Message ID of next message we're waiting to receive
|
|
|
|
uint16_t msgid = msgid_cache[cache_index].next_receive;
|
|
|
|
|
|
|
|
frame[16] = msgid >> 8;
|
|
|
|
frame[17] = msgid & 0xff;
|
|
|
|
|
|
|
|
send_frame(frame, sizeof(frame));
|
|
|
|
}
|
|
|
|
|
2019-07-09 17:22:55 +00:00
|
|
|
void send_message(void) {
|
|
|
|
unsigned char frame[14 + 2 + 2 + 2 + own_message_length];
|
|
|
|
|
|
|
|
write_headers(frame, own_message_destination_mac, EMT_MESSAGE);
|
|
|
|
|
|
|
|
// Message ID
|
|
|
|
frame[16] = own_message_msgid >> 8;
|
|
|
|
frame[17] = own_message_msgid & 0xff;
|
|
|
|
|
|
|
|
// Message length
|
|
|
|
frame[18] = own_message_length >> 8;
|
|
|
|
frame[19] = own_message_length & 0xff;
|
|
|
|
|
|
|
|
// Message
|
|
|
|
memcpy(&frame[20], own_message, own_message_length);
|
|
|
|
|
|
|
|
send_frame(frame, sizeof(frame));
|
|
|
|
}
|
|
|
|
|
|
|
|
void send_ack(const unsigned char destination[6], uint16_t msgid) {
|
|
|
|
unsigned char frame[14 + 2 + 2];
|
|
|
|
|
|
|
|
write_headers(frame, destination, EMT_ACK);
|
|
|
|
|
|
|
|
// Message ID
|
|
|
|
frame[16] = msgid >> 8;
|
|
|
|
frame[17] = msgid & 0xff;
|
|
|
|
|
|
|
|
send_frame(frame, sizeof(frame));
|
|
|
|
}
|
|
|
|
|
2019-07-10 19:10:14 +00:00
|
|
|
void readallx(int fd, unsigned char *buf, size_t length) {
|
|
|
|
size_t completed = 0;
|
|
|
|
while (completed < length) {
|
|
|
|
ssize_t res = read(fd, &buf[completed], length - completed);
|
|
|
|
if (res == -1) {
|
|
|
|
err(1, "read");
|
2019-07-14 21:38:14 +00:00
|
|
|
} else if (res == 0) {
|
|
|
|
errx(1, "Unexpected EOF");
|
2019-07-10 19:10:14 +00:00
|
|
|
}
|
|
|
|
completed += res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-13 17:51:15 +00:00
|
|
|
void writeallx(int fd, const void *buf, size_t length) {
|
|
|
|
const unsigned char *cbuf = buf;
|
2019-07-10 19:10:14 +00:00
|
|
|
size_t completed = 0;
|
|
|
|
while (completed < length) {
|
2019-07-13 17:51:15 +00:00
|
|
|
ssize_t res = write(fd, &cbuf[completed], length - completed);
|
2019-07-10 19:10:14 +00:00
|
|
|
if (res == -1) {
|
|
|
|
err(1, "write");
|
|
|
|
}
|
|
|
|
completed += res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-14 17:26:29 +00:00
|
|
|
void writeallx_u16(int fd, uint16_t value) {
|
|
|
|
unsigned char buf[2];
|
2019-07-14 20:13:39 +00:00
|
|
|
buf[0] = value >> 8;
|
|
|
|
buf[1] = value & 0xff;
|
2019-07-14 17:26:29 +00:00
|
|
|
writeallx(fd, buf, sizeof(buf));
|
|
|
|
}
|
|
|
|
|
2019-07-15 21:43:22 +00:00
|
|
|
bool check_utf8(const unsigned char *data, size_t data_length, bool newline_tab_allowed);
|
2019-07-10 19:44:20 +00:00
|
|
|
|
2019-07-10 19:10:14 +00:00
|
|
|
void read_status(void) {
|
|
|
|
unsigned char status;
|
|
|
|
readallx(0, &status, 1);
|
|
|
|
if (status != EMS_AVAILABLE && status != EMS_UNAVAILABLE && status != EMS_OFFLINE) {
|
|
|
|
errx(1, "Frontend sent a status %u that we don't understand", status);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char length;
|
|
|
|
readallx(0, &length, 1);
|
|
|
|
|
2019-07-10 20:49:05 +00:00
|
|
|
unsigned char nick[length];
|
2019-07-10 19:44:20 +00:00
|
|
|
readallx(0, nick, length);
|
|
|
|
|
|
|
|
if (!check_utf8(nick, length, false)) {
|
|
|
|
errx(1, "Frontend sent a nick with malformed utf-8 or control characters");
|
|
|
|
}
|
2019-07-10 19:10:14 +00:00
|
|
|
|
2019-07-14 20:25:10 +00:00
|
|
|
own_status = status;
|
|
|
|
|
2019-07-10 19:44:20 +00:00
|
|
|
memcpy(own_nick, nick, length);
|
2019-07-10 19:10:14 +00:00
|
|
|
own_nick_length = length;
|
|
|
|
}
|
|
|
|
|
2019-07-10 20:49:05 +00:00
|
|
|
void read_message(void) {
|
2019-07-14 22:01:16 +00:00
|
|
|
if (own_message_send_state != IDLE) {
|
|
|
|
errx(1, "Frontend sent a new message while we are still processing the old one");
|
|
|
|
}
|
|
|
|
|
2019-07-10 20:49:05 +00:00
|
|
|
unsigned char mac[6];
|
|
|
|
readallx(0, mac, sizeof(mac));
|
|
|
|
|
|
|
|
unsigned char length_raw[2];
|
|
|
|
readallx(0, length_raw, 2);
|
|
|
|
size_t length = (length_raw[0] << 8) | length_raw[1];
|
|
|
|
|
|
|
|
if (length > EM_MESSAGE_MAX_LENGTH) {
|
|
|
|
errx(1, "Frontend sent a message that is too long (%zuB, max is %uB)", length, EM_MESSAGE_MAX_LENGTH);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char message[length];
|
|
|
|
readallx(0, message, length);
|
|
|
|
|
|
|
|
if (!check_utf8(message, length, true)) {
|
|
|
|
errx(1, "Frontend sent a nick with malformed utf-8 or control characters other than newline");
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(own_message_destination_mac, mac, sizeof(own_message_destination_mac));
|
|
|
|
memcpy(own_message, message, length);
|
|
|
|
own_message_length = length;
|
|
|
|
own_message_send_state = QUEUED;
|
|
|
|
}
|
|
|
|
|
2019-07-06 17:26:09 +00:00
|
|
|
void read_command(void) {
|
2019-07-10 20:37:39 +00:00
|
|
|
unsigned char cmd;
|
|
|
|
readallx(0, &cmd, 1);
|
2019-07-09 17:22:55 +00:00
|
|
|
|
2019-07-06 17:26:09 +00:00
|
|
|
if (cmd == 'q') {
|
|
|
|
running = false;
|
2019-07-14 20:13:39 +00:00
|
|
|
} else if (cmd == 'r') {
|
2019-07-10 20:37:39 +00:00
|
|
|
unsigned char mac[6];
|
|
|
|
readallx(0, mac, sizeof(mac));
|
|
|
|
send_status_request(mac);
|
2019-07-14 20:13:39 +00:00
|
|
|
} else if (cmd == 's') {
|
2019-07-10 20:49:05 +00:00
|
|
|
read_status();
|
2019-07-14 20:25:10 +00:00
|
|
|
send_status(broadcast_mac);
|
2019-07-09 17:22:55 +00:00
|
|
|
} else if (cmd == 'm') {
|
2019-07-10 20:49:05 +00:00
|
|
|
read_message();
|
2019-07-06 17:26:09 +00:00
|
|
|
} else {
|
2019-07-14 19:54:17 +00:00
|
|
|
errx(1, "Frontend sent an unknown command %c", cmd);
|
2019-07-06 17:26:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-09 13:22:55 +00:00
|
|
|
bool check_padding(const unsigned char *data, size_t index, size_t data_length) {
|
|
|
|
// Valid padding is all zero bytes
|
|
|
|
assert(index <= data_length);
|
|
|
|
for (size_t i = index; i < data_length; i++) {
|
|
|
|
if (data[i] != 0) {
|
|
|
|
// Check failed
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check succeeded
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-15 21:43:22 +00:00
|
|
|
bool check_utf8(const unsigned char *data, size_t data_length, bool newline_tab_allowed) {
|
2019-07-10 18:20:24 +00:00
|
|
|
size_t remaining = 0;
|
|
|
|
size_t length = 0;
|
|
|
|
uint32_t codepoint = 0;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < data_length; i++) {
|
|
|
|
unsigned char byte = data[i];
|
|
|
|
|
|
|
|
if (byte <= 0x7f) {
|
|
|
|
// 0xxxxxxx - single byte
|
|
|
|
if (remaining != 0) {
|
|
|
|
// Can't appear in the middle of a multibyte sequence
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
remaining = 0;
|
|
|
|
length = 1;
|
|
|
|
codepoint = byte;
|
|
|
|
} else if (byte <= 0xbf) {
|
|
|
|
// 10xxxxxx - continuation byte
|
|
|
|
if (remaining == 0) {
|
|
|
|
// Can only appear in the middle of a multibyte sequence
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
remaining--;
|
|
|
|
length++;
|
|
|
|
codepoint <<= 6;
|
|
|
|
codepoint |= byte & 0x3f;
|
|
|
|
} else if (byte <= 0xdf) {
|
|
|
|
// 110xxxxx - first byte of double byte sequence
|
|
|
|
if (remaining != 0) {
|
|
|
|
// Can't appear in the middle of a multibyte sequence
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
remaining = 1;
|
|
|
|
length = 1;
|
|
|
|
codepoint = byte & 0x1f;
|
|
|
|
} else if (byte <= 0xef) {
|
|
|
|
// 1110xxxx - first byte of triple byte sequence
|
|
|
|
if (remaining != 0) {
|
|
|
|
// Can't appear in the middle of a multibyte sequence
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
remaining = 2;
|
|
|
|
length = 1;
|
|
|
|
codepoint = byte & 0x0f;
|
|
|
|
} else if (byte <= 0xf7) {
|
|
|
|
// 11110xxx - first byte of quadruple byte sequence
|
|
|
|
if (remaining != 0) {
|
|
|
|
// Can't appear in the middle of a multibyte sequence
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
remaining = 3;
|
|
|
|
length = 1;
|
|
|
|
codepoint = byte & 0x07;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (remaining == 0) {
|
|
|
|
// Full codepoint constructed
|
|
|
|
|
|
|
|
// Reject overlong encodings
|
|
|
|
if (codepoint <= 0x007f && length > 1) {
|
|
|
|
return false;
|
|
|
|
} else if (codepoint <= 0x07ff && length > 2) {
|
|
|
|
return false;
|
|
|
|
} else if (codepoint <= 0xffff && length > 3) {
|
|
|
|
return false;
|
|
|
|
} else if (codepoint <= 0x10ffff && length > 4) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reject code points over U+10FFFF
|
|
|
|
if (codepoint > 0x10ffff) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reject surrogate pairs
|
|
|
|
if (0xd800 <= codepoint && codepoint <= 0xdfff) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reject non-characters
|
2019-07-10 18:21:46 +00:00
|
|
|
if ((codepoint & 0xffff) == 0xfffe || (codepoint & 0xffff) == 0xffff) {
|
2019-07-10 18:20:24 +00:00
|
|
|
// Plane end non-characters
|
|
|
|
return false;
|
|
|
|
} else if (0xfdd0 <= codepoint && codepoint <= 0xfdef) {
|
|
|
|
// BMP non-character block
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reject control characters
|
|
|
|
if (codepoint <= 0x1f) {
|
|
|
|
// C0 control character
|
2019-07-15 21:43:22 +00:00
|
|
|
if (!newline_tab_allowed || (codepoint != 0x0a && codepoint != 0x09)) {
|
2019-07-10 18:20:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (0x80 <= codepoint && codepoint <= 0x9f) {
|
|
|
|
// C1 control character
|
|
|
|
return false;
|
|
|
|
} else if (codepoint == 0x2028) {
|
|
|
|
// U+2028 LINE SEPARATOR
|
|
|
|
return false;
|
|
|
|
} else if (codepoint == 0x2029) {
|
|
|
|
// U+2029 PARAGRAPH SEPARATOR
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (remaining != 0) {
|
|
|
|
// Can't end at the middle of a multibyte sequence
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-06 21:21:11 +00:00
|
|
|
void handle_status(const unsigned char source_mac[6], const unsigned char *data, size_t data_length) {
|
|
|
|
if (data_length < 2) {
|
|
|
|
// Too short
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char status = data[0];
|
|
|
|
|
2019-07-09 16:09:17 +00:00
|
|
|
if (status != EMS_AVAILABLE && status != EMS_UNAVAILABLE && status != EMS_OFFLINE) {
|
2019-07-06 21:21:11 +00:00
|
|
|
// Unknown status, throw away
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char nick_length = data[1];
|
|
|
|
|
|
|
|
if (nick_length > data_length - 2) {
|
|
|
|
// Malformed length field
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-09 17:22:55 +00:00
|
|
|
unsigned char nick[nick_length];
|
2019-07-06 21:21:11 +00:00
|
|
|
memcpy(nick, &data[2], nick_length);
|
|
|
|
|
2019-07-09 13:22:55 +00:00
|
|
|
if (!check_padding(data, 2 + nick_length, data_length)) {
|
|
|
|
// Malformed padding
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-10 18:20:24 +00:00
|
|
|
if (!check_utf8(nick, nick_length, false)) {
|
|
|
|
// Malformed utf-8, or has control chars
|
|
|
|
return;
|
|
|
|
}
|
2019-07-07 16:37:51 +00:00
|
|
|
|
2019-07-13 17:51:15 +00:00
|
|
|
// Type of event: Status
|
|
|
|
writeallx(1, "s", 1);
|
2019-07-06 21:21:11 +00:00
|
|
|
|
2019-07-13 17:51:15 +00:00
|
|
|
// MAC
|
|
|
|
writeallx(1, source_mac, 6);
|
2019-07-06 21:21:11 +00:00
|
|
|
|
2019-07-13 17:51:15 +00:00
|
|
|
// Status
|
|
|
|
writeallx(1, &status, 1);
|
2019-07-06 21:21:11 +00:00
|
|
|
|
2019-07-13 17:51:15 +00:00
|
|
|
// Nick
|
|
|
|
writeallx(1, &nick_length, 1);
|
|
|
|
writeallx(1, nick, nick_length);
|
2019-07-06 21:21:11 +00:00
|
|
|
}
|
|
|
|
|
2019-07-07 16:37:51 +00:00
|
|
|
void handle_msgid(const unsigned char source_mac[6], const unsigned char *data, size_t data_length) {
|
|
|
|
if (data_length < 2) {
|
|
|
|
// Too short
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-09 17:22:55 +00:00
|
|
|
uint16_t msgid = (data[0] << 8) | data[1];
|
2019-07-07 16:37:51 +00:00
|
|
|
|
2019-07-09 13:22:55 +00:00
|
|
|
if (!check_padding(data, 2, data_length)) {
|
|
|
|
// Malformed padding
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-07 16:37:51 +00:00
|
|
|
ssize_t cache_index = msgid_cache_lookup(source_mac);
|
|
|
|
if (cache_index == -1) {
|
|
|
|
// Not in the cache, so add it there
|
|
|
|
cache_index = msgid_cache_add(source_mac);
|
|
|
|
}
|
|
|
|
|
2019-07-10 13:22:02 +00:00
|
|
|
if (msgid_cache[cache_index].know_send) {
|
|
|
|
// There is sth in the cache, test whether we should update it
|
|
|
|
uint16_t diff = msgid - msgid_cache[cache_index].next_send;
|
|
|
|
if (diff < 0x8000) {
|
|
|
|
// See the description in handle_message for what is going on
|
|
|
|
msgid_cache[cache_index].next_send = msgid;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Nothing in the cache
|
|
|
|
msgid_cache[cache_index].next_send = msgid;
|
|
|
|
msgid_cache[cache_index].know_send = true;
|
|
|
|
}
|
2019-07-07 16:37:51 +00:00
|
|
|
}
|
|
|
|
|
2019-07-09 17:22:55 +00:00
|
|
|
void handle_message(const unsigned char source_mac[6], const unsigned char *data, size_t data_length) {
|
|
|
|
if (data_length < 4) {
|
|
|
|
// Too short
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t msgid = (data[0] << 8) | data[1];
|
|
|
|
|
|
|
|
uint16_t message_length = (data[2] << 8) | data[3];
|
|
|
|
|
|
|
|
if (message_length > data_length - 4 || message_length > EM_MESSAGE_MAX_LENGTH) {
|
|
|
|
// Malformed length field
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char message[message_length];
|
|
|
|
memcpy(message, &data[4], message_length);
|
|
|
|
|
|
|
|
if (!check_padding(data, 4 + message_length, data_length)) {
|
|
|
|
// Malformed padding
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-10 18:20:24 +00:00
|
|
|
if (!check_utf8(message, message_length, true)) {
|
|
|
|
// Malformed utf-8, or has control chars other than newline
|
|
|
|
return;
|
|
|
|
}
|
2019-07-09 17:22:55 +00:00
|
|
|
|
2019-07-10 11:00:44 +00:00
|
|
|
// See whether we've already received this message and update the next msgid if so
|
|
|
|
ssize_t cache_index = msgid_cache_lookup(source_mac);
|
|
|
|
if (cache_index == -1) {
|
|
|
|
// No cache entry -> add an empty one
|
|
|
|
cache_index = msgid_cache_add(source_mac);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!msgid_cache[cache_index].know_receive) {
|
|
|
|
// We have a cache entry, but no idea what next message to receive
|
|
|
|
// Assume this is not a repeat transmission, and accept the message
|
|
|
|
// The easiest way to do this is to set this message's msgid as the
|
|
|
|
// one we're looking to receive
|
|
|
|
msgid_cache[cache_index].next_receive = msgid;
|
|
|
|
msgid_cache[cache_index].know_receive = true;
|
|
|
|
}
|
|
|
|
|
2019-07-10 13:22:02 +00:00
|
|
|
uint16_t diff = msgid - msgid_cache[cache_index].next_receive;
|
|
|
|
if (diff < 0x8000) {
|
2019-07-10 11:00:44 +00:00
|
|
|
// The msgid counter can wrap around, so a simple larger than
|
|
|
|
// comparison will not work. Instead, we look at the distance
|
|
|
|
// between the message's msgid and the one we're expecting to
|
|
|
|
// receive. If it is from 0 to 0x8000, we consider it to be
|
|
|
|
// in the forwards direction, and if it is more, we consider it
|
|
|
|
// to be in the backwards one. This leaves equally sized (2^15)
|
|
|
|
// ranges for values in both cases.
|
|
|
|
|
|
|
|
// In this case the msgid is considered to be of a message we
|
|
|
|
// haven't yet seen. Therefore we'll continue processing, and
|
|
|
|
// update the next msgid we're expecting to receive to this one
|
|
|
|
// plus 1
|
|
|
|
msgid_cache[cache_index].next_receive = msgid + 1;
|
|
|
|
} else {
|
|
|
|
// In this case we consider the message already received
|
|
|
|
// We will send an ack, but not process it any further
|
|
|
|
send_ack(source_mac, msgid);
|
|
|
|
return;
|
|
|
|
}
|
2019-07-09 17:22:55 +00:00
|
|
|
|
2019-07-14 17:36:12 +00:00
|
|
|
// Type of event: Received a message
|
|
|
|
writeallx(1, "m", 1);
|
2019-07-09 17:22:55 +00:00
|
|
|
|
2019-07-14 17:36:12 +00:00
|
|
|
// MAC
|
|
|
|
writeallx(1, source_mac, 6);
|
2019-07-09 17:22:55 +00:00
|
|
|
|
2019-07-14 17:36:12 +00:00
|
|
|
// Message length
|
|
|
|
writeallx_u16(1, message_length);
|
2019-07-09 17:22:55 +00:00
|
|
|
|
2019-07-14 17:36:12 +00:00
|
|
|
// Message
|
|
|
|
writeallx(1, message, message_length);
|
2019-07-09 17:22:55 +00:00
|
|
|
|
|
|
|
send_ack(source_mac, msgid);
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_ack(const unsigned char source_mac[6], const unsigned char *data, size_t data_length) {
|
|
|
|
if (data_length < 2) {
|
|
|
|
// Too short
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t msgid = (data[0] << 8) | data[1];
|
|
|
|
|
|
|
|
if (!check_padding(data, 2, data_length)) {
|
|
|
|
// Malformed padding
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-14 17:39:45 +00:00
|
|
|
// Type of event: ACK received
|
|
|
|
writeallx(1, "a", 1);
|
2019-07-09 17:22:55 +00:00
|
|
|
|
2019-07-14 17:39:45 +00:00
|
|
|
// MAC
|
|
|
|
writeallx(1, source_mac, 6);
|
2019-07-09 17:22:55 +00:00
|
|
|
|
2019-07-14 17:39:45 +00:00
|
|
|
// Msgid
|
|
|
|
writeallx_u16(1, msgid);
|
2019-07-13 16:39:11 +00:00
|
|
|
|
2019-07-10 13:22:02 +00:00
|
|
|
if (own_message_send_state == WAITING_ACK && msgid == own_message_msgid) {
|
2019-07-09 17:22:55 +00:00
|
|
|
own_message_send_state = IDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-06 17:26:09 +00:00
|
|
|
void process_frame(void) {
|
|
|
|
unsigned char frame[1518]; // Largest a 802.3 frame can be without FCS
|
|
|
|
|
2019-07-06 21:21:11 +00:00
|
|
|
ssize_t res = recv(packet_socket, frame, sizeof(frame), 0);
|
|
|
|
if (res == -1) {
|
2019-07-09 13:35:48 +00:00
|
|
|
err(1, "recv");
|
2019-07-06 21:21:11 +00:00
|
|
|
} else if (res < 16) {
|
|
|
|
// Frame too short to contain enough information
|
|
|
|
return;
|
2019-07-06 17:26:09 +00:00
|
|
|
}
|
2019-07-06 21:21:11 +00:00
|
|
|
size_t packet_length = (size_t)res;
|
2019-07-06 17:26:09 +00:00
|
|
|
|
2019-07-06 21:21:11 +00:00
|
|
|
// Check that the packet is Ethermess (EtherType DA7A)
|
|
|
|
if (frame[12] != 0xda || frame[13] != 0x7a) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (memcmp(frame, own_mac, 6) == 0) {
|
2019-07-06 17:41:31 +00:00
|
|
|
// Targetted at us
|
2019-07-06 21:21:11 +00:00
|
|
|
} else if (memcmp(frame, broadcast_mac, 6) == 0) {
|
2019-07-06 17:41:31 +00:00
|
|
|
// Broadcast
|
|
|
|
} else {
|
2019-07-06 21:21:11 +00:00
|
|
|
// Does not concern us
|
2019-07-06 17:41:31 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-07-06 21:21:11 +00:00
|
|
|
|
|
|
|
// Extract source MAC
|
|
|
|
unsigned char source_mac[6];
|
|
|
|
memcpy(source_mac, &frame[6], sizeof(source_mac));
|
|
|
|
|
|
|
|
// Extract version
|
|
|
|
unsigned char version = frame[14];
|
|
|
|
|
|
|
|
// If they speak a version we don't understand, tell them to speak ours
|
|
|
|
if (version > EM_PROTOCOL_VERSION) {
|
|
|
|
send_speak_version(source_mac);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extract Ethermess packet type
|
|
|
|
unsigned char packet_type = frame[15];
|
|
|
|
|
|
|
|
// Process the packet based on the packet type
|
|
|
|
switch (packet_type) {
|
|
|
|
case EMT_STATUS_REQUEST:
|
2019-07-09 13:22:55 +00:00
|
|
|
if (check_padding(&frame[16], 0, packet_length - 16)) {
|
|
|
|
send_status(source_mac);
|
|
|
|
}
|
2019-07-06 21:21:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EMT_STATUS:
|
|
|
|
handle_status(source_mac, &frame[16], packet_length - 16);
|
|
|
|
break;
|
|
|
|
|
2019-07-07 16:37:51 +00:00
|
|
|
case EMT_MSGID_REQUEST:
|
2019-07-09 13:22:55 +00:00
|
|
|
if (check_padding(&frame[16], 0, packet_length - 16)) {
|
|
|
|
send_msgid(source_mac);
|
|
|
|
}
|
2019-07-07 16:37:51 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EMT_MSGID:
|
|
|
|
handle_msgid(source_mac, &frame[16], packet_length - 16);
|
|
|
|
break;
|
|
|
|
|
2019-07-09 17:22:55 +00:00
|
|
|
case EMT_MESSAGE:
|
|
|
|
handle_message(source_mac, &frame[16], packet_length - 16);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EMT_ACK:
|
|
|
|
handle_ack(source_mac, &frame[16], packet_length - 16);
|
|
|
|
break;
|
2019-07-06 21:21:11 +00:00
|
|
|
}
|
2019-07-06 17:26:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void eventloop(void) {
|
|
|
|
// Listen on both stdin for commands and network interface for packets
|
|
|
|
struct pollfd pollfds[2];
|
|
|
|
|
|
|
|
// stdin
|
|
|
|
pollfds[0].fd = 0;
|
|
|
|
pollfds[0].events = POLLIN;
|
|
|
|
|
|
|
|
// Network interface
|
|
|
|
pollfds[1].fd = packet_socket;
|
|
|
|
pollfds[1].events = POLLIN;
|
|
|
|
|
|
|
|
while (running) {
|
2019-07-10 13:22:02 +00:00
|
|
|
int retransmit_wait_ms = INT_MAX;
|
|
|
|
|
2019-07-09 17:22:55 +00:00
|
|
|
// (Attempt) to process a message send
|
|
|
|
if (own_message_send_state == QUEUED) {
|
|
|
|
// We need to have the correct msgid to be able to send
|
|
|
|
ssize_t cache_index = msgid_cache_lookup(own_message_destination_mac);
|
|
|
|
|
|
|
|
if (cache_index == -1 || !msgid_cache[cache_index].know_send) {
|
|
|
|
// We don't know what the msgid should be
|
|
|
|
// -> ask the other side
|
|
|
|
send_msgid_request(own_message_destination_mac);
|
2019-07-10 13:22:02 +00:00
|
|
|
// Wait around 1 to 1.5s before asking again
|
|
|
|
next_retransmission = ms_in_future(EM_RETRANSMIT_TIME);
|
|
|
|
retransmission_count = 0;
|
|
|
|
own_message_send_state = WAITING_MSGID;
|
2019-07-09 17:22:55 +00:00
|
|
|
} else {
|
|
|
|
// It is in the cache
|
|
|
|
own_message_msgid = msgid_cache[cache_index].next_send++;
|
|
|
|
own_message_send_state = SENDING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-10 13:22:02 +00:00
|
|
|
if (own_message_send_state == WAITING_MSGID) {
|
|
|
|
ssize_t cache_index = msgid_cache_lookup(own_message_destination_mac);
|
|
|
|
|
|
|
|
if (cache_index == -1 || !msgid_cache[cache_index].know_send) {
|
|
|
|
// Still no msgid
|
|
|
|
retransmit_wait_ms = wait_ms_until(next_retransmission);
|
|
|
|
|
|
|
|
if (retransmit_wait_ms == 0 && retransmission_count < EM_MAX_RETRANSMIT) {
|
|
|
|
// Time to resend
|
|
|
|
send_msgid_request(own_message_destination_mac);
|
|
|
|
// Wait around 1 to 1.5s before asking again
|
|
|
|
next_retransmission = ms_in_future(EM_RETRANSMIT_TIME);
|
|
|
|
retransmission_count++;
|
|
|
|
} else if (retransmit_wait_ms == 0 && retransmission_count >= EM_MAX_RETRANSMIT) {
|
|
|
|
// Time to give up
|
|
|
|
own_message_send_state = IDLE;
|
|
|
|
|
2019-07-14 19:49:12 +00:00
|
|
|
// Type of event: Msgid failed
|
|
|
|
writeallx(1, "I", 1);
|
2019-07-10 13:22:02 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Found msgid
|
|
|
|
own_message_msgid = msgid_cache[cache_index].next_send++;
|
|
|
|
own_message_send_state = SENDING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-09 17:22:55 +00:00
|
|
|
if (own_message_send_state == SENDING) {
|
2019-07-14 17:26:29 +00:00
|
|
|
// Type of event: msgid for a message
|
|
|
|
writeallx(1, "i", 1);
|
|
|
|
|
|
|
|
// Msgid
|
|
|
|
writeallx_u16(1, own_message_msgid);
|
|
|
|
|
|
|
|
// Send message
|
2019-07-09 17:22:55 +00:00
|
|
|
send_message();
|
2019-07-14 17:26:29 +00:00
|
|
|
|
2019-07-10 13:22:02 +00:00
|
|
|
// Wait around 1 to 1.5 before sending again
|
|
|
|
next_retransmission = ms_in_future(EM_RETRANSMIT_TIME);
|
|
|
|
retransmission_count = 0;
|
|
|
|
own_message_send_state = WAITING_ACK;
|
2019-07-09 17:22:55 +00:00
|
|
|
}
|
|
|
|
|
2019-07-10 13:22:02 +00:00
|
|
|
if (own_message_send_state == WAITING_ACK) {
|
|
|
|
retransmit_wait_ms = wait_ms_until(next_retransmission);
|
|
|
|
|
|
|
|
if (retransmit_wait_ms == 0 && retransmission_count < EM_MAX_RETRANSMIT) {
|
|
|
|
// Time to resend
|
|
|
|
send_message();
|
|
|
|
// Wait around 1 to 1.5 before sending again
|
|
|
|
next_retransmission = ms_in_future(EM_RETRANSMIT_TIME);
|
|
|
|
retransmission_count++;
|
|
|
|
} else if (retransmit_wait_ms == 0 && retransmission_count >= EM_MAX_RETRANSMIT) {
|
|
|
|
// Time to give up
|
|
|
|
own_message_send_state = IDLE;
|
|
|
|
|
2019-07-14 19:49:12 +00:00
|
|
|
// Type of event: ACK not received
|
|
|
|
writeallx(1, "A", 1);
|
2019-07-10 13:22:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process status broadcasting
|
|
|
|
int status_broadcast_wait_ms = wait_ms_until(next_status_broadcast);
|
|
|
|
if (status_broadcast_wait_ms == 0) {
|
2019-07-09 14:18:00 +00:00
|
|
|
// The time has come to send the status broadcast
|
|
|
|
send_status(broadcast_mac);
|
2019-07-13 16:41:35 +00:00
|
|
|
// Do next one in about a minute
|
2019-07-10 13:22:02 +00:00
|
|
|
next_status_broadcast = ms_in_future(EM_STATUS_BROADCAST_TIME);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Figure out how many ms to wait
|
|
|
|
int wait_ms;
|
|
|
|
if (retransmit_wait_ms < status_broadcast_wait_ms) {
|
|
|
|
wait_ms = retransmit_wait_ms;
|
|
|
|
} else {
|
|
|
|
wait_ms = status_broadcast_wait_ms;
|
2019-07-09 14:18:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int ready = poll(pollfds, sizeof(pollfds) / sizeof(*pollfds), wait_ms);
|
2019-07-06 17:26:09 +00:00
|
|
|
if (ready == -1) {
|
|
|
|
err(1, "poll");
|
|
|
|
}
|
|
|
|
|
|
|
|
// stdin
|
|
|
|
if (ready > 0 && pollfds[0].revents != 0) {
|
|
|
|
ready--;
|
|
|
|
|
|
|
|
if (pollfds[0].revents & POLLIN) {
|
|
|
|
// Read a command
|
|
|
|
read_command();
|
2019-07-10 20:49:05 +00:00
|
|
|
} else if (pollfds[0].revents & POLLHUP) {
|
2019-07-13 16:34:54 +00:00
|
|
|
// Quit on frontend exiting
|
2019-07-13 16:55:26 +00:00
|
|
|
exit(1);
|
2019-07-06 17:26:09 +00:00
|
|
|
} else {
|
|
|
|
errx(1, "Got poll event %hd on stdin\n", pollfds[0].revents);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// packet_socket
|
|
|
|
if (ready > 0 && pollfds[1].revents != 0) {
|
|
|
|
ready--;
|
|
|
|
|
|
|
|
if (pollfds[1].revents & POLLIN) {
|
|
|
|
// Process a frame
|
|
|
|
process_frame();
|
2019-07-13 16:34:54 +00:00
|
|
|
} else if (pollfds[1].revents & POLLERR) {
|
|
|
|
// Lost connection
|
|
|
|
errx(1, "Network went down");
|
2019-07-06 17:26:09 +00:00
|
|
|
} else {
|
|
|
|
errx(1, "Got poll event %hd on packet socket\n", pollfds[1].revents);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ready > 0) {
|
|
|
|
errx(1, "poll(1) says we have ready fds, but neither was ready");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
if (argc != 2) {
|
|
|
|
fprintf(stderr, "Usage: %s interface\n", argv[0]);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *interface_name = argv[1];
|
|
|
|
|
2019-07-09 13:35:48 +00:00
|
|
|
// Open /dev/urandom for getting randomness
|
|
|
|
urandom = open("/dev/urandom", O_RDONLY);
|
|
|
|
|
2019-07-06 17:26:09 +00:00
|
|
|
// Create a packet socket
|
|
|
|
packet_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
|
|
|
if (packet_socket == -1) {
|
|
|
|
err(1, "socket");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only creating the socket requires root privs
|
|
|
|
drop_privileges();
|
|
|
|
|
|
|
|
// Find the index of the network interface
|
|
|
|
struct ifreq ifr;
|
|
|
|
strncpy(ifr.ifr_name, interface_name, IFNAMSIZ);
|
|
|
|
if (ioctl(packet_socket, SIOCGIFINDEX, &ifr) == -1) {
|
|
|
|
err(1, "ioctl");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bind to the network interface
|
|
|
|
struct sockaddr_ll sll;
|
|
|
|
sll.sll_family = AF_PACKET;
|
|
|
|
sll.sll_protocol = htons(ETH_P_ALL);
|
|
|
|
sll.sll_ifindex = ifr.ifr_ifindex;
|
|
|
|
if (bind(packet_socket, (const struct sockaddr*)&sll, sizeof(sll)) == -1) {
|
|
|
|
err(1, "bind");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get our own MAC
|
|
|
|
strncpy(ifr.ifr_name, interface_name, IFNAMSIZ);
|
|
|
|
if (ioctl(packet_socket, SIOCGIFHWADDR, &ifr) == -1) {
|
|
|
|
err(1, "ioctl");
|
|
|
|
}
|
|
|
|
if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
|
|
|
|
errx(1, "Not an Ethernet interface");
|
|
|
|
}
|
|
|
|
memcpy(own_mac, ifr.ifr_hwaddr.sa_data, sizeof(own_mac));
|
|
|
|
|
|
|
|
// Print it out
|
2019-07-13 16:34:54 +00:00
|
|
|
if (write(1, own_mac, 6) != 6) {
|
|
|
|
err(1, "write");
|
2019-07-10 13:28:22 +00:00
|
|
|
}
|
2019-07-06 17:26:09 +00:00
|
|
|
|
2019-07-07 16:37:51 +00:00
|
|
|
// Initialize the message id cache
|
|
|
|
memset(msgid_cache, 0, sizeof(msgid_cache));
|
|
|
|
|
2019-07-10 19:10:14 +00:00
|
|
|
// Set our status and nick
|
|
|
|
read_status();
|
|
|
|
|
2019-07-09 14:18:00 +00:00
|
|
|
// Broadcast our status to the network to let them know we're here
|
|
|
|
send_status(broadcast_mac);
|
|
|
|
|
2019-07-13 16:41:35 +00:00
|
|
|
// Schedule next broadcast of our status about 1 min in the future
|
2019-07-10 13:22:02 +00:00
|
|
|
next_status_broadcast = ms_in_future(EM_STATUS_BROADCAST_TIME);
|
2019-07-09 14:18:00 +00:00
|
|
|
|
2019-07-06 21:21:11 +00:00
|
|
|
// Request status from everyone, so that we can get an idea of who is on the network
|
|
|
|
send_status_request(broadcast_mac);
|
|
|
|
|
2019-07-06 17:26:09 +00:00
|
|
|
// Start the event loop
|
|
|
|
eventloop();
|
|
|
|
|
2019-07-10 13:24:27 +00:00
|
|
|
// Set our status to going offline and broadcast that
|
|
|
|
own_status = EMS_OFFLINE;
|
|
|
|
send_status(broadcast_mac);
|
|
|
|
|
2019-07-06 17:26:09 +00:00
|
|
|
// Close the socket (tho I'm not 100% sure it's needed)
|
|
|
|
if (close(packet_socket) == -1) {
|
|
|
|
err(1, "close");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Flush stdout
|
|
|
|
if (fflush(stdout) == EOF) {
|
|
|
|
err(1, "fflush");
|
|
|
|
}
|
|
|
|
|
2019-07-09 13:35:48 +00:00
|
|
|
// Close urandom
|
|
|
|
if (close(urandom) == -1) {
|
|
|
|
err(1, "close");
|
|
|
|
}
|
|
|
|
|
2019-07-06 17:26:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|