aboutsummaryrefslogtreecommitdiff
path: root/source/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/server.c')
-rw-r--r--source/server.c197
1 files changed, 99 insertions, 98 deletions
diff --git a/source/server.c b/source/server.c
index a6613d6..689537e 100644
--- a/source/server.c
+++ b/source/server.c
@@ -13,11 +13,11 @@
#ifndef Assert
#ifdef DEBUG
#define Assert(expr) if (!(expr)) { \
- raise(SIGTRAP); \
+raise(SIGTRAP); \
}
#else
#define Assert(expr) if (!(expr)) { \
- raise(SIGTRAP); \
+raise(SIGTRAP); \
}
#endif // DEBUG
#endif // Assert
@@ -42,8 +42,9 @@
#define FDS_SIZE (fdsArena.pos / sizeof(struct pollfd))
#define CLIENTS_SIZE (clientsArena.pos / sizeof(Client))
+#define IMPORT_ID 1
// Where to save clients
-#define CLIENTS_FILE "_clients"
+#define CLIENTS_FILE ".chatty_clients"
// Where to write logs
#define LOGFILE "server.log"
// Log to LOGFILE instead of stderr
@@ -51,8 +52,8 @@
// enum for indexing the fds array
enum { FDS_STDIN = 0,
- FDS_SERVER,
- FDS_CLIENTS };
+ FDS_SERVER,
+ FDS_CLIENTS };
// Client information
typedef struct {
@@ -80,7 +81,7 @@ Client*
getClientByID(Client* clients, u32 nclients, ID id)
{
if (!id) return 0;
-
+
for (u32 i = 0; i < nclients; i++)
{
if (clients[i].id == id)
@@ -95,7 +96,7 @@ Client*
getClientByFD(Client* clients, u32 nclients, s32 fd)
{
if (fd == -1) return 0;
-
+
for (u32 i = 0; i < nclients; i++)
{
if ((clients[i].unifd && clients[i].unifd->fd == fd) ||
@@ -111,7 +112,7 @@ printTextMessage(TextMessage* message, Client* client, u8 wide)
{
u8 timestamp[TIMESTAMP_LEN] = {0};
formatTimestamp(timestamp, message->timestamp);
-
+
if (wide)
{
setlocale(LC_ALL, "");
@@ -134,7 +135,7 @@ sendToOthers(Client* clients, u32 nclients, Client* client, ClientFD type, Heade
for (u32 i = 0; i < nclients - 1; i ++)
{
if (clients + i == client) continue;
-
+
if (type == UNIFD)
{
if (clients[i].unifd && clients[i].unifd->fd != -1)
@@ -150,7 +151,7 @@ sendToOthers(Client* clients, u32 nclients, Client* client, ClientFD type, Heade
continue;
}
nsend = sendAnyMessage(fd, *header, anyMessage);
-
+
assert(nsend != -1);
LoggingF("sendToOthers "CLIENT_FMT"|%d<-%s %d bytes\n", CLIENT_ARG((clients[i])), fd, headerTypeString(header->type), nsend);
}
@@ -183,8 +184,8 @@ sendToAll(Client* clients, u32 nclients, ClientFD type, HeaderMessage* header, v
assert(0);
assert(nsend != -1);
LoggingF("sendToAll|[%s]->"CLIENT_FMT" %d bytes\n", headerTypeString(header->type),
- CLIENT_ARG(clients[i]),
- nsend);
+ CLIENT_ARG(clients[i]),
+ nsend);
}
}
@@ -213,7 +214,7 @@ void
disconnectAndNotify(Client* clients, u32 nclients, Client* client)
{
disconnect(client);
-
+
local_persist HeaderMessage header = HEADER_INIT(HEADER_TYPE_PRESENCE);
header.id = client->id;
PresenceMessage message = {.type = PRESENCE_TYPE_DISCONNECTED};
@@ -231,16 +232,16 @@ authenticate(Arena* clientsArena, s32 clients_file, struct pollfd* pollfd, Heade
{
s32 nrecv = 0;
Client* client = 0;
-
+
LoggingF("authenticate (%d)|" HEADER_FMT "\n", pollfd->fd, HEADER_ARG(header));
-
+
/* Scenario 1: Search for existing client */
if (header.type == HEADER_TYPE_ID)
{
IDMessage message;
s32 nrecv = recv(pollfd->fd, &message, sizeof(message), 0);
assert(nrecv == sizeof(message));
-
+
client = getClientByID((Client*)clientsArena->addr, nclients, message.id);
if (!client)
{
@@ -257,15 +258,15 @@ authenticate(Arena* clientsArena, s32 clients_file, struct pollfd* pollfd, Heade
ErrorMessage error_message = ERROR_INIT(ERROR_TYPE_SUCCESS);
sendAnyMessage(pollfd->fd, header, &error_message);
}
-
+
if (!client->bifd)
client->bifd = pollfd;
else if (!client->unifd)
client->unifd = pollfd;
else
assert(0);
-
-
+
+
return client;
}
/* Scenario 2: Create a new client */
@@ -278,40 +279,40 @@ authenticate(Arena* clientsArena, s32 clients_file, struct pollfd* pollfd, Heade
LoggingF("authenticate (%d)|err: %d/%lu bytes\n", pollfd->fd, nrecv, sizeof(message));
return 0;
}
-
+
// Copy metadata from IntroductionMessage
client = ArenaPush(clientsArena, sizeof(*client));
memcpy(client->author, message.author, AUTHOR_LEN);
client->id = nclients;
-
+
if (!client->bifd)
client->bifd = pollfd;
else if (!client->unifd)
client->unifd = pollfd;
else
assert(0);
-
+
nclients++;
-
+
#ifdef IMPORT_ID
write(clients_file, client, sizeof(*client));
#endif
LoggingF("authenticate (%d)|Added [%s](%lu)\n", pollfd->fd, client->author, client->id);
-
+
// Send ID to new client
HeaderMessage header = HEADER_INIT(HEADER_TYPE_ID);
IDMessage id_message;
id_message.id = client->id;
-
+
s32 nsend = sendAnyMessage(pollfd->fd, header, &id_message);
assert(nsend != -1);
-
+
return client;
}
-
+
LoggingF("authenticate (%d)|Wrong header expected %s or %s\n", pollfd->fd,
- headerTypeString(HEADER_TYPE_INTRODUCTION),
- headerTypeString(HEADER_TYPE_ID));
+ headerTypeString(HEADER_TYPE_INTRODUCTION),
+ headerTypeString(HEADER_TYPE_ID));
return 0;
}
@@ -319,19 +320,19 @@ int
main(int argc, char** argv)
{
signal(SIGPIPE, SIG_IGN);
-
+
LogFD = 2;
// optional logging
if (argc > 1)
{
if (*argv[1] == '-')
if (argv[1][1] == 'l')
- {
- LogFD = open(LOGFILE, O_RDWR | O_CREAT | O_TRUNC, 0600);
- assert(LogFD != -1);
- }
+ {
+ LogFD = open(LOGFILE, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ assert(LogFD != -1);
+ }
}
-
+
s32 serverfd;
// Start listening on the socket
{
@@ -339,25 +340,25 @@ main(int argc, char** argv)
u32 on = 1;
serverfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
assert(serverfd > 2);
-
+
err = setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, (u8*)&on, sizeof(on));
assert(!err);
-
+
const struct sockaddr_in address = {
AF_INET,
htons(PORT),
{0},
{0},
};
-
+
err = bind(serverfd, (const struct sockaddr*)&address, sizeof(address));
assert(!err);
-
+
err = listen(serverfd, MAX_CONNECTIONS);
assert(!err);
LoggingF("Listening on :%d\n", PORT);
}
-
+
Arena clientsArena;
Arena fdsArena;
Arena msgsArena;
@@ -366,7 +367,7 @@ main(int argc, char** argv)
ArenaAlloc(&msgsArena, Megabytes(128)); // storing received messages
struct pollfd* fds = fdsArena.addr;
Client* clients = clientsArena.addr;
-
+
// Initializing fds
struct pollfd* fdsAddr;
struct pollfd newpollfd = {-1, POLLIN, 0}; // for copying with events already set
@@ -379,21 +380,21 @@ main(int argc, char** argv)
fdsAddr = ArenaPush(&fdsArena, sizeof(*fds));
memcpy(fdsAddr, &newpollfd, sizeof(*fds));
newpollfd.fd = -1;
-
+
s32 clients_file;
#ifdef IMPORT_ID
clients_file = open(CLIENTS_FILE, O_RDWR | O_CREAT | O_APPEND, 0600);
assert(clients_file != -1);
struct stat statbuf;
assert(fstat(clients_file, &statbuf) != -1);
-
+
read(clients_file, clients, statbuf.st_size);
if (statbuf.st_size > 0)
{
ArenaPush(&clientsArena, statbuf.st_size);
LoggingF("Imported %lu client(s)\n", statbuf.st_size / sizeof(*clients));
nclients += statbuf.st_size / sizeof(*clients);
-
+
// Reset pointers on imported clients
for (u32 i = 0; i < nclients - 1; i++)
{
@@ -406,16 +407,16 @@ main(int argc, char** argv)
#else
clients_file = 0;
#endif
-
+
// Initialize the rest of the fds array
for (u32 i = FDS_CLIENTS; i < MAX_CONNECTIONS; i++)
fds[i] = newpollfd;
-
+
while (1)
{
s32 err = poll(fds, FDS_SIZE, TIMEOUT);
assert(err != -1);
-
+
if (fds[FDS_STDIN].revents & POLLIN)
{
u8 c; // exit on ctrl-d
@@ -426,7 +427,7 @@ main(int argc, char** argv)
{
// TODO: what if we are not aligned by 2 anymore?
s32 clientfd = accept(serverfd, 0, 0);
-
+
if (clientfd == -1)
{
LoggingF("Error while accepting connection (%d)\n", clientfd);
@@ -434,7 +435,7 @@ main(int argc, char** argv)
}
else
LoggingF("New connection(%d)\n", clientfd);
-
+
// TODO: find empty space in arena (fragmentation)
if (nclients + 1 == MAX_CONNECTIONS)
{
@@ -453,13 +454,13 @@ main(int argc, char** argv)
LoggingF("Added pollfd(%d)\n", clientfd);
}
}
-
+
for (u32 conn = FDS_CLIENTS; conn < FDS_SIZE; conn++)
{
if (!(fds[conn].revents & POLLIN)) continue;
if (fds[conn].fd == -1) continue;
LoggingF("Message(%d)\n", fds[conn].fd);
-
+
// We received a message, try to parse the header
HeaderMessage header;
s32 nrecv = recv(fds[conn].fd, &header, sizeof(header), 0);
@@ -467,7 +468,7 @@ main(int argc, char** argv)
{
LoggingF("Received error from fd: %d, errno: %d\n", fds[conn].fd, errno);
};
-
+
Client* client;
if (nrecv != sizeof(header))
{
@@ -486,14 +487,14 @@ main(int argc, char** argv)
continue;
}
LoggingF("Received(%d): " HEADER_FMT "\n", fds[conn].fd, HEADER_ARG(header));
-
+
// Authentication
if (!header.id)
{
LoggingF("No client for connection(%d)\n", fds[conn].fd);
-
+
client = authenticate(&clientsArena, clients_file, fds + conn, header);
-
+
if (!client)
{
LoggingF("Could not initialize client (%d)\n", fds[conn].fd);
@@ -511,71 +512,71 @@ main(int argc, char** argv)
}
continue;
}
-
+
client = getClientByID(clients, nclients, header.id);
if (!client)
{
LoggingF("No client for id %d\n", fds[conn].fd);
-
+
header.type = HEADER_TYPE_ERROR;
ErrorMessage message = ERROR_INIT(ERROR_TYPE_NOTFOUND);
-
+
sendAnyMessage(fds[conn].fd, header, &message);
-
+
// Reject connection
fds[conn].fd = -1;
close(fds[conn].fd);
continue;
}
-
+
switch (header.type) {
- /* Send text message to all other clients */
- case HEADER_TYPE_TEXT:
- {
- TextMessage* text_message = recvTextMessage(&msgsArena, fds[conn].fd);
- LoggingF("Received(%d): ", fds[conn].fd);
- printTextMessage(text_message, client, 0);
-
- sendToOthers(clients, nclients, client, UNIFD, &header, text_message);
- } break;
- /* Send back client information */
- case HEADER_TYPE_ID:
- {
- IDMessage id_message;
- s32 nrecv = recv(fds[conn].fd, &id_message, sizeof(id_message), 0);
- assert(nrecv == sizeof(id_message));
-
- client = getClientByID(clients, nclients, id_message.id);
- if (!client)
+ /* Send text message to all other clients */
+ case HEADER_TYPE_TEXT:
{
- header.type = HEADER_TYPE_ERROR;
- ErrorMessage message = ERROR_INIT(ERROR_TYPE_NOTFOUND);
- s32 nsend = sendAnyMessage(fds[conn].fd, header, &message);
- assert(nsend != -1);
- break;
- }
-
- HeaderMessage header = HEADER_INIT(HEADER_TYPE_INTRODUCTION);
- IntroductionMessage introduction_message;
- header.id = client->id;
- memcpy(introduction_message.author, client->author, AUTHOR_LEN);
-
- nrecv = sendAnyMessage(fds[conn].fd, header, &introduction_message);
- assert(nrecv != -1);
- } break;
- default:
+ TextMessage* text_message = recvTextMessage(&msgsArena, fds[conn].fd);
+ LoggingF("Received(%d): ", fds[conn].fd);
+ printTextMessage(text_message, client, 0);
+
+ sendToOthers(clients, nclients, client, UNIFD, &header, text_message);
+ } break;
+ /* Send back client information */
+ case HEADER_TYPE_ID:
+ {
+ IDMessage id_message;
+ s32 nrecv = recv(fds[conn].fd, &id_message, sizeof(id_message), 0);
+ assert(nrecv == sizeof(id_message));
+
+ client = getClientByID(clients, nclients, id_message.id);
+ if (!client)
+ {
+ header.type = HEADER_TYPE_ERROR;
+ ErrorMessage message = ERROR_INIT(ERROR_TYPE_NOTFOUND);
+ s32 nsend = sendAnyMessage(fds[conn].fd, header, &message);
+ assert(nsend != -1);
+ break;
+ }
+
+ HeaderMessage header = HEADER_INIT(HEADER_TYPE_INTRODUCTION);
+ IntroductionMessage introduction_message;
+ header.id = client->id;
+ memcpy(introduction_message.author, client->author, AUTHOR_LEN);
+
+ nrecv = sendAnyMessage(fds[conn].fd, header, &introduction_message);
+ assert(nrecv != -1);
+ } break;
+ default:
LoggingF("Unhandled '%s' from "CLIENT_FMT"(%d)\n", headerTypeString(header.type),
- CLIENT_ARG((*client)),
- fds[conn].fd);
+ CLIENT_ARG((*client)),
+ fds[conn].fd);
disconnectAndNotify(client, nclients, client);
continue;
}
}
}
-
+
#ifdef IMPORT_ID
close(clients_file);
#endif
-
+
return 0;
}