zsm

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 297f55bc60c2371050bf0cdf6d43615c60540704
Author: night0721 <[email protected]>
Date:   Tue, 30 Apr 2024 17:17:17 +0100

Initial commit

Diffstat:
A.gitignore | 4++++
AMakefile | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME.md | 22++++++++++++++++++++++
Ainclude/notification.h | 8++++++++
Ainclude/packet.h | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/notification.c | 8++++++++
Alib/packet.c | 273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/client/client.c | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/server/server.c | 218+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 801 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,4 @@ +zsm +zsmc +*.o +*.tar.gz diff --git a/Makefile b/Makefile @@ -0,0 +1,52 @@ +.POSIX: +.SUFFIXES: + +CC = cc +VERSION = 1.0 +SERVER = zsm +CLIENT = zsmc +MANPAGE = $(TARGET).1 +PREFIX ?= /usr/local +BINDIR = $(PREFIX)/bin +MANDIR = $(PREFIX)/share/man/man1 + +# Flags +LDFLAGS = $(shell pkg-config --libs libsodium libnotify ncurses) +CFLAGS = -O3 -mtune=native -march=native -pipe -g -std=c99 -Wno-pointer-sign -Wpedantic -Wall -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 $(shell pkg-config --cflags libsodium libnotify ncurses) -lpthread + +SERVERSRC = src/server/*.c +CLIENTSRC = src/client/*.c +LIBSRC = lib/*.c +INCLUDE = -Iinclude/ + +$(SERVER): $(SERVERSRC) $(LIBSRC) + $(CC) $(SERVERSRC) $(LIBSRC) $(INCLUDE) -o $@ $(CFLAGS) $(LDFLAGS) + +$(CLIENT): $(CLIENTSRC) $(LIBSRC) + $(CC) $(CLIENTSRC) $(LIBSRC) $(INCLUDE) -o $@ $(CFLAGS) $(LDFLAGS) + +dist: + mkdir -p $(TARGET)-$(VERSION) + cp -R README.md $(MANPAGE) $(TARGET) $(TARGET)-$(VERSION) + tar -cf $(TARGET)-$(VERSION).tar $(TARGET)-$(VERSION) + gzip $(TARGET)-$(VERSION).tar + rm -rf $(TARGET)-$(VERSION) + +install: $(TARGET) + mkdir -p $(DESTDIR)$(BINDIR) + mkdir -p $(DESTDIR)$(MANDIR) + cp -p $(TARGET) $(DESTDIR)$(BINDIR)/$(TARGET) + chmod 755 $(DESTDIR)$(BINDIR)/$(TARGET) + cp -p $(MANPAGE) $(DESTDIR)$(MANDIR)/$(MANPAGE) + chmod 644 $(DESTDIR)$(MANDIR)/$(MANPAGE) + +uninstall: + $(RM) $(DESTDIR)$(BINDIR)/$(TARGET) + $(RM) $(DESTDIR)$(MANDIR)/$(MANPAGE) + +clean: + $(RM) $(TARGET) + +all: $(TARGET) + +.PHONY: all dist install uninstall clean diff --git a/README.md b/README.md @@ -0,0 +1,22 @@ +# zsm + +Zen Secure Messaging(zsm) is a secure messaging protocol specifically for Linux(Unix-based) systems. + +## Dependencies +- sqlite +- libsodium +- libnotify + +## Building + +> You will need to run these with elevated privilages. +> You will need `*-dev` packages to build both server and client. + +```sh +git clone https://github.com/night0721/zsm +make +make install +``` + +## License +This project is licensed under the GNU Public License v3.0. See [LICENSE](https://github.com/night0721/zsm/blob/master/LICENSE) for more information. diff --git a/include/notification.h b/include/notification.h @@ -0,0 +1,8 @@ +#ifndef NOTIFICATION_H +#define NOTIFICATION_H + +#include <libnotify/notify.h> + +void send_notification(const char *content); + +#endif diff --git a/include/packet.h b/include/packet.h @@ -0,0 +1,69 @@ +#ifndef PACKET_H +#define PACKET_H + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sodium.h> + +#define DEBUG 1 +#define DOMAIN "127.0.0.1" +#define PORT 20247 +#define MAX_CONNECTION 5 +#define MAX_MESSAGE_LENGTH 8192 +#define ERROR_LENGTH 26 + +#define ZSM_TYP_KEY 0x1 +#define ZSM_TYP_SEND_MESSAGE 0x2 +#define ZSM_TYP_UPDATE_MESSAGE 0x3 +#define ZSM_TYP_DELETE_MESSAGE 0x4 +#define ZSM_TYP_PRESENCE 0x5 +#define ZSM_TYP_TYPING 0x6 +#define ZSM_TYP_ERROR 0x7 /* Error message */ +#define ZSM_TYP_B 0x8 + +#define ZSM_STA_SUCCESS 0x1 +#define ZSM_STA_INVALID_TYPE 0x2 +#define ZSM_STA_INVALID_LENGTH 0x3 +#define ZSM_STA_TOO_LONG 0x4 +#define ZSM_STA_READING_SOCKET 0x5 +#define ZSM_STA_WRITING_SOCKET 0x6 +#define ZSM_STA_UNKNOWN_USER 0x7 +#define ZSM_STA_MEMORY_ALLOCATION 0x8 +#define ZSM_STA_WRONG_KEY_LENGTH 0x9 + +#define PUBLIC_KEY_SIZE crypto_kx_PUBLICKEYBYTES +#define PRIVATE_KEY_SIZE crypto_kx_SECRETKEYBYTES +#define SHARED_KEY_SIZE crypto_kx_SESSIONKEYBYTES +#define NONCE_SIZE crypto_aead_xchacha20poly1305_ietf_NPUBBYTES +#define ADDITIONAL_SIZE crypto_aead_xchacha20poly1305_ietf_ABYTES + +typedef struct message { + uint8_t option; + uint8_t type; + unsigned long long length; + unsigned char *data; +} message; + +/* Utilities functions */ +void error(int fatal, const char *fmt, ...); +void *memalloc(size_t size); +void *estrdup(void *str); +unsigned char *get_public_key(int sockfd); +int send_public_key(int sockfd, unsigned char *pk); +void print_packet(message *msg); +int recv_packet(message *msg, int fd); +message *create_error_packet(int code); +message *create_packet(uint8_t option, uint8_t type, uint32_t length, char *data); +int send_packet(message *msg, int fd); +void free_packet(message *msg); + +#endif diff --git a/lib/notification.c b/lib/notification.c @@ -0,0 +1,8 @@ +#include "notification.h" + +void send_notification(const char *content) +{ + NotifyNotification *noti = notify_notification_new("Client", content, "dialog-information"); + notify_notification_show(noti, NULL); + g_object_unref(G_OBJECT(noti)); +} diff --git a/lib/packet.c b/lib/packet.c @@ -0,0 +1,273 @@ +#include "packet.h" + +/* + * msg is the error message to print to stderr + * will include error message from function if errno isn't 0 + * end program is fatal is 1 + */ +void error(int fatal, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + /* to preserve errno */ + int errsv = errno; + + /* Determine the length of the formatted error message */ + va_list args_copy; + va_copy(args_copy, args); + size_t error_len = vsnprintf(NULL, 0, fmt, args_copy); + va_end(args_copy); + + /* 7 for [zsm], space and null */ + char errorstr[error_len + 1]; + vsnprintf(errorstr, error_len + 1, fmt, args); + fprintf(stderr, "[zsm] "); + + if (errsv != 0) { + perror(errorstr); + errno = 0; + } else { + fprintf(stderr, "%s\n", errorstr); + } + + va_end(args); + if (fatal) exit(1); +} + +void *memalloc(size_t size) +{ + void *ptr = malloc(size); + if (!ptr) { + error(0, "Error allocating memory"); + return NULL; + } + return ptr; +} + +void *estrdup(void *str) +{ + void *modstr = strdup(str); + if (modstr == NULL) { + error(0, "Error allocating memory"); + return NULL; + } + return modstr; +} + +uint8_t *get_public_key(int sockfd) +{ + message keyex_msg; + if (recv_packet(&keyex_msg, sockfd) != ZSM_STA_SUCCESS) { + /* We can't do anything if key exchange already failed */ + close(sockfd); + return NULL; + } else { + int status = 0; + /* Check to see if the content is actually a key */ + if (keyex_msg.type != ZSM_TYP_KEY) { + status = ZSM_STA_INVALID_TYPE; + } + if (keyex_msg.length != PUBLIC_KEY_SIZE) { + status = ZSM_STA_WRONG_KEY_LENGTH; + } + if (status != 0) { + free(keyex_msg.data); + message *error_msg = create_error_packet(status); + send_packet(error_msg, sockfd); + free_packet(error_msg); + close(sockfd); + return NULL; + } + } + /* Obtain public key from packet */ + uint8_t *pk = memalloc(PUBLIC_KEY_SIZE * sizeof(char)); + memcpy(pk, keyex_msg.data, PUBLIC_KEY_SIZE); + if (pk == NULL) { + free(keyex_msg.data); + /* Fatal, we couldn't complete key exchange */ + close(sockfd); + return NULL; + } + free(keyex_msg.data); + return pk; +} + +int send_public_key(int sockfd, uint8_t *pk) +{ + /* send_packet requires heap allocated buffer */ + uint8_t *pk_dup = memalloc(PUBLIC_KEY_SIZE * sizeof(char)); + memcpy(pk_dup, pk, PUBLIC_KEY_SIZE); + if (pk_dup == NULL) { + close(sockfd); + return -1; + } + + /* Sending our public key to client */ + /* option???? */ + message *keyex = create_packet(1, ZSM_TYP_KEY, PUBLIC_KEY_SIZE, pk_dup); + send_packet(keyex, sockfd); + free_packet(keyex); + return 0; +} + +void print_packet(message *msg) +{ + printf("Option: %d\n", msg->option); + printf("Type: %d\n", msg->type); + printf("Length: %lld\n", msg->length); + printf("Data: %s\n\n", msg->data); +} + +/* + * Requires manually free message data + */ +int recv_packet(message *msg, int fd) +{ + int status = ZSM_STA_SUCCESS; + + /* Read the message components */ + if (recv(fd, &msg->option, sizeof(msg->option), 0) < 0 || + recv(fd, &msg->type, sizeof(msg->type), 0) < 0 || + recv(fd, &msg->length, sizeof(msg->length), 0) < 0) { + status = ZSM_STA_READING_SOCKET; + error(0, "Error reading from socket"); + } + #if DEBUG == 1 + printf("==========PACKET RECEIVED==========\n"); + #endif + #if DEBUG == 1 + printf("Option: %d\n", msg->option); + #endif + + if (msg->type > 0xFF || msg->type < 0x0) { + status = ZSM_STA_INVALID_TYPE; + error(0, "Invalid message type"); + goto failure; + } + #if DEBUG == 1 + printf("Type: %d\n", msg->type); + #endif + + /* Convert message length from network byte order to host byte order */ + if (msg->length > MAX_MESSAGE_LENGTH) { + status = ZSM_STA_TOO_LONG; + error(0, "Message too long: %lld", msg->length); + goto failure; + } + #if DEBUG == 1 + printf("Length: %lld\n", msg->length); + #endif + + // Allocate memory for message data + msg->data = memalloc((msg->length + 1) * sizeof(char)); + if (msg->data == NULL) { + status = ZSM_STA_MEMORY_ALLOCATION; + goto failure; + } + + /* Read message data from the socket */ + size_t bytes_read = 0; + if ((bytes_read = recv(fd, msg->data, msg->length, 0)) < 0) { + status = ZSM_STA_READING_SOCKET; + error(0, "Error reading from socket"); + free(msg->data); + goto failure; + } + if (bytes_read != msg->length) { + status = ZSM_STA_INVALID_LENGTH; + error(0, "Invalid message length: bytes_read=%ld != msg->length=%lld", bytes_read, msg->length); + free(msg->data); + goto failure; + } + msg->data[msg->length] = '\0'; + + #if DEBUG == 1 + printf("Data: %s\n\n", msg->data); + #endif + + return status; +failure:; + message *error_msg = create_error_packet(status); + if (send_packet(error_msg, fd) != ZSM_STA_SUCCESS) { + /* Resend it? */ + error(0, "Failed to send error packet to peer. Error status => %d", status); + } + free_packet(error_msg); + return status; +} + +message *create_error_packet(int code) +{ + char *err = memalloc(ERROR_LENGTH * sizeof(char)); + switch (code) { + case ZSM_STA_INVALID_TYPE: + strcpy(err, "Invalid message type "); + break; + case ZSM_STA_INVALID_LENGTH: + strcpy(err, "Invalid message length "); + break; + case ZSM_STA_TOO_LONG: + strcpy(err, "Message too long "); + break; + case ZSM_STA_READING_SOCKET: + strcpy(err, "Error reading from socket"); + break; + case ZSM_STA_WRITING_SOCKET: + strcpy(err, "Error writing to socket "); + break; + case ZSM_STA_UNKNOWN_USER: + strcpy(err, "Unknwon user "); + break; + case ZSM_STA_WRONG_KEY_LENGTH: + strcpy(err, "Wrong public key length "); + break; + } + return create_packet(1, ZSM_TYP_ERROR, ERROR_LENGTH, err); +} + +/* + * Requires heap allocated msg data + */ +message *create_packet(uint8_t option, uint8_t type, uint32_t length, char *data) +{ + message *msg = memalloc(sizeof(message)); + msg->option = option; + msg->type = type; + msg->length = length; + msg->data = data; + return msg; +} + +/* + * Requires heap allocated msg data + */ +int send_packet(message *msg, int fd) +{ + int status = ZSM_STA_SUCCESS; + uint32_t length = msg->length; + // Send the message back to the client + if (send(fd, &msg->option, sizeof(msg->option), 0) <= 0 || + send(fd, &msg->type, sizeof(msg->type), 0) <= 0 || + send(fd, &msg->length, sizeof(msg->length), 0) <= 0 || + send(fd, msg->data, length, 0) <= 0) { + status = ZSM_STA_WRITING_SOCKET; + error(0, "Error writing to socket"); + //free(msg->data); + close(fd); // Close the socket and continue accepting connections + } + #if DEBUG == 1 + printf("==========PACKET SENT==========\n"); + print_packet(msg); + #endif + return status; +} + +void free_packet(message *msg) +{ + if (msg->type != 0x10) { + /* temp solution, dont use stack allocated msg to send to client */ + free(msg->data); + } + free(msg); +} diff --git a/src/client/client.c b/src/client/client.c @@ -0,0 +1,147 @@ +#include "packet.h" +#include <pthread.h> + +uint8_t shared_key[SHARED_KEY_SIZE]; +int sockfd; + +/* + * Connect to socket server + */ +int socket_init() +{ + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + error(1, "Error on opening socket"); + } + + struct hostent *server = gethostbyname(DOMAIN); + if (server == NULL) { + error(1, "No such host %s", DOMAIN); + } + + struct sockaddr_in sv_addr; + memset(&sv_addr, 0, sizeof(sv_addr)); + sv_addr.sin_family = AF_INET; + sv_addr.sin_port = htons(PORT); + memcpy(&sv_addr.sin_addr.s_addr, server->h_addr, server->h_length); + +/* free(server); */ + if (connect(sockfd, (struct sockaddr *) &sv_addr, sizeof(sv_addr)) < 0) { + error(1, "Error on connect"); + close(sockfd); + return 0; + } + printf("Connected to server at %s\n", DOMAIN); + return sockfd; +} + +/* + * Performs key exchange with server + */ +int key_exchange(int sockfd) +{ + /* Generate the client's key pair */ + uint8_t cl_pk[PUBLIC_KEY_SIZE], cl_sk[PRIVATE_KEY_SIZE]; + crypto_kx_keypair(cl_pk, cl_sk); + + /* Send our public key */ + if (send_public_key(sockfd, cl_pk) < 0) { + return -1; + } + + /* Get public key from server */ + uint8_t *pk; + if ((pk = get_public_key(sockfd)) == NULL) { + return -1; + } + + /* Compute a shared key using the server's public key and our secret key */ + if (crypto_kx_client_session_keys(NULL, shared_key, cl_pk, cl_sk, pk) != 0) { + error(1, "Server public key is not acceptable"); + free(pk); + close(sockfd); + return -1; + } + free(pk); + return 0; +} + +void *sender() +{ + while (1) { + printf("Enter message to send to server: "); + fflush(stdout); + char line[1024]; + line[0] = '\0'; + size_t length = strlen(line); + while (length <= 1) { + fgets(line, sizeof(line), stdin); + length = strlen(line); + } + length -= 1; + line[length] = '\0'; + + uint8_t nonce[NONCE_SIZE]; + uint8_t encrypted[length + ADDITIONAL_SIZE]; + unsigned long long encrypted_len; + + randombytes_buf(nonce, sizeof(nonce)); + crypto_aead_xchacha20poly1305_ietf_encrypt(encrypted, &encrypted_len, + line, length, + NULL, 0, NULL, nonce, shared_key); + size_t payload_t = NONCE_SIZE + encrypted_len; + uint8_t encryptedwithnonce[payload_t]; + memcpy(encryptedwithnonce, nonce, NONCE_SIZE); + memcpy(encryptedwithnonce + NONCE_SIZE, encrypted, encrypted_len); + + message *msg = create_packet(1, 0x10, payload_t, encryptedwithnonce); + if (send_packet(msg, sockfd) != ZSM_STA_SUCCESS) { + close(sockfd); + } + free_packet(msg); + } + close(sockfd); + +} + +void *receiver() +{ + while (1) { + message servermsg; + if (recv_packet(&servermsg, sockfd) != ZSM_STA_SUCCESS) { + close(sockfd); + return 0; + } + free(servermsg.data); + } + return NULL; +} + +int main() +{ + if (sodium_init() < 0) { + error(1, "Error initializing libsodium"); + } + sockfd = socket_init(); + if (key_exchange(sockfd) < 0) { + /* Fatal */ + error(1, "Error performing key exchange with server"); + } + pthread_t recv_worker, send_worker; + if (pthread_create(&recv_worker, NULL, sender, NULL) != 0) { + fprintf(stderr, "Error creating incoming thread\n"); + return 1; + } + + if (pthread_create(&send_worker, NULL, receiver, NULL) != 0) { + fprintf(stderr, "Error creating outgoing thread\n"); + return 1; + } + + // Join threads + pthread_join(recv_worker, NULL); + pthread_join(send_worker, NULL); + + return 0; +} + diff --git a/src/server/server.c b/src/server/server.c @@ -0,0 +1,218 @@ +#include "packet.h" +#include "notification.h" +#include <pthread.h> + +socklen_t clilen; +struct sockaddr_in cli_address; +uint8_t shared_key[SHARED_KEY_SIZE]; +int clientfd; + +/* + * Initialise socket server + */ +int socket_init() +{ + int serverfd = socket(AF_INET, SOCK_STREAM, 0); + if (serverfd < 0) { + error(1, "Error on opening socket"); + } + + /* Reuse addr(for debug) */ + int optval = 1; + if (setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { + + error(1, "Error at setting SO_REUSEADDR"); + } + + struct sockaddr_in sv_addr; + memset(&sv_addr, 0, sizeof(sv_addr)); + sv_addr.sin_family = AF_INET; + sv_addr.sin_addr.s_addr = INADDR_ANY; + sv_addr.sin_port = htons(PORT); + + if (bind(serverfd, (struct sockaddr *) &sv_addr, sizeof(sv_addr)) < 0) { + error(1, "Error on bind"); + } + + if (listen(serverfd, MAX_CONNECTION) < 0) { + error(1, "Error on listen"); + } + + printf("Listening on port %d\n", PORT); + clilen = sizeof(cli_address); + return serverfd; +} + +/* + * Performs key exchange with client + */ +int key_exchange(int clientfd) +{ + /* Generate the server's key pair */ + uint8_t sv_pk[PUBLIC_KEY_SIZE], sv_sk[PRIVATE_KEY_SIZE]; + crypto_kx_keypair(sv_pk, sv_sk); + + /* Get public key from client */ + uint8_t *pk; + if ((pk = get_public_key(clientfd)) == NULL) { + return -1; + } + + /* Send our public key */ + if (send_public_key(clientfd, sv_pk) < 0) { + free(pk); + return -1; + } + + /* Compute a shared key using the client's public key and our secret key. */ + if (crypto_kx_server_session_keys(NULL, shared_key, sv_pk, sv_sk, pk) != 0) { + error(0, "Client public key is not acceptable"); + free(pk); + close(clientfd); + return -1; + } + + free(pk); + return 0; +} + +void signal_handler(int signal) +{ + switch (signal) { + case SIGPIPE: + error(0, "SIGPIPE received"); + break; + case SIGABRT: + case SIGINT: + case SIGTERM: + notify_uninit(); + error(1, "Shutdown signal received"); + break; + } +} + +void *receiver() +{ + int serverfd = socket_init(); + clientfd = accept(serverfd, (struct sockaddr *) &cli_address, &clilen); + if (clientfd < 0) { + error(0, "Error on accepting client"); + /* Continue accpeting connections */ +/* continue; */ + } + + if (key_exchange(clientfd) < 0) { + error(0, "Error performing key exchange with client"); +/* continue; */ + } + while (1) { + message msg; + memset(&msg, 0, sizeof(msg)); + if (recv_packet(&msg, clientfd) != ZSM_STA_SUCCESS) { + close(clientfd); + break; +/* continue; */ + } + + size_t encrypted_len = msg.length - NONCE_SIZE; + size_t msg_len = encrypted_len - ADDITIONAL_SIZE; + uint8_t nonce[NONCE_SIZE]; + uint8_t encrypted[encrypted_len]; + uint8_t decrypted[msg_len + 1]; + unsigned long long decrypted_len; + memcpy(nonce, msg.data, NONCE_SIZE); + memcpy(encrypted, msg.data + NONCE_SIZE, encrypted_len); + + free(msg.data); + if (crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted, &decrypted_len, + NULL, + encrypted, encrypted_len, + NULL, 0, + nonce, shared_key) != 0) { + error(0, "Cannot decrypt message"); + } else { + /* Decrypted message */ + decrypted[msg_len] = '\0'; + printf("Decrypted: %s\n", decrypted); + send_notification(decrypted); + msg.data = malloc(14); + strcpy(msg.data, "Received data"); + msg.length = 14; + send_packet(&msg, clientfd); + free(msg.data); + } + } + close(clientfd); + close(serverfd); + return NULL; +} + +void *sender() +{ + while (1) { + printf("Enter message to send to client: "); + fflush(stdout); + char line[1024]; + line[0] = '\0'; + size_t length = strlen(line); + while (length <= 1) { + fgets(line, sizeof(line), stdin); + length = strlen(line); + } + length -= 1; + line[length] = '\0'; + + uint8_t nonce[NONCE_SIZE]; + uint8_t encrypted[length + ADDITIONAL_SIZE]; + unsigned long long encrypted_len; + + randombytes_buf(nonce, sizeof(nonce)); + crypto_aead_xchacha20poly1305_ietf_encrypt(encrypted, &encrypted_len, + line, length, + NULL, 0, NULL, nonce, shared_key); + size_t payload_t = NONCE_SIZE + encrypted_len; + uint8_t encryptedwithnonce[payload_t]; + memcpy(encryptedwithnonce, nonce, NONCE_SIZE); + memcpy(encryptedwithnonce + NONCE_SIZE, encrypted, encrypted_len); + + message *msg = create_packet(1, 0x10, payload_t, encryptedwithnonce); + if (send_packet(msg, clientfd) != ZSM_STA_SUCCESS) { + close(clientfd); + } + free_packet(msg); + } + close(clientfd); + return NULL; +} + +int main() +{ + if (sodium_init() < 0) { + error(1, "Error initializing libsodium"); + } + /* Init libnotify with app name */ + if (notify_init("zsm") < 0) { + error(1, "Error initializing libnotify"); + } + signal(SIGPIPE, signal_handler); + signal(SIGABRT, signal_handler); + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + pthread_t recv_worker, send_worker; + if (pthread_create(&recv_worker, NULL, sender, NULL) != 0) { + fprintf(stderr, "Error creating incoming thread\n"); + return 1; + } + + if (pthread_create(&send_worker, NULL, receiver, NULL) != 0) { + fprintf(stderr, "Error creating outgoing thread\n"); + return 1; + } + + // Join threads + pthread_join(recv_worker, NULL); + pthread_join(send_worker, NULL); + + return 0; +}