diff --git a/client.c b/client.c index 1e34481..219ebad 100644 --- a/client.c +++ b/client.c @@ -1,4 +1,5 @@ #include +#include void client_close(client_t *client) { diff --git a/client.h b/client.h index e5bd25a..b27befd 100644 --- a/client.h +++ b/client.h @@ -1,6 +1,21 @@ -/* Handles communication with clients. */ +#pragma once -#include +/* Handles communication with a client. */ + +#include +#include + +#define USERNAME_LEN 32 + +typedef struct +{ + int socket; /* client socket */ + struct sockaddr_in address; /* client address information */ + char ip_string[INET_ADDRSTRLEN]; /* client IP address as a string */ + time_t last_active; /* time of client's last activity in seconds from Epoch */ + char username[USERNAME_LEN]; /* username for human identification */ + char data[DATABUF_LEN]; /* buffer for data received from client */ +} client_t; /** * Closes a client connection. diff --git a/common.h b/common.h new file mode 100644 index 0000000..7b3e270 --- /dev/null +++ b/common.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#define DATABUF_LEN 128 + +#define TIMESTAMP_FORMAT "%Y-%m-%dT%H:%M:%S" /* ISO 8601 */ +#define TIMESTAMP_LEN 20+1 /* calculated according to the timestamp format */ + +/* Global variables used throughout the application. */ +int debug_messages; /* if > 0 debug messages will be printed */ + +/* Global functions used throughout the application. */ + +/** + * Converts time in seconds from Epoch to a conveniently formatted string. + */ +void time2string(time_t time, char* timestamp); diff --git a/moxerver.c b/moxerver.c index 5c2332d..fd8edf2 100644 --- a/moxerver.c +++ b/moxerver.c @@ -11,8 +11,6 @@ #define NAME "moxerver" -#define SERVER_WAIT_TIMEOUT 2 /* seconds for select() timeout in server loop */ - /* Prints help message. */ static void usage() { //TODO maybe some styling should be done @@ -44,12 +42,6 @@ void quit_handler(int signum) { cleanup(0); } -/* Converts from time in seconds from Epoch to conveniently formatted string. */ -int time2string(time_t time, char* timestamp) { - strftime(timestamp, TIMESTAMP_LEN, TIMESTAMP_FORMAT, localtime(&time)); - return 0; -} - /* Parse handler function, used to configure serial port */ int parse_handler(void *user, const char *section, const char *name, const char *value) { @@ -71,18 +63,241 @@ int parse_handler(void *user, const char *section, const char *name, const char return 1; } +void time2string(time_t time, char* timestamp) +{ + strftime(timestamp, TIMESTAMP_LEN, TIMESTAMP_FORMAT, localtime(&time)); +} + +void* thread_new_client_connection(void *args) +{ + char msg[DATABUF_LEN]; + char timestamp[TIMESTAMP_LEN]; + client_t temp_client; + + /* get resources from args */ + resources_t r = *((resources_t*) args); + + /* accept new connection request */ + if (server_accept(r.server, &temp_client) != 0) + { + return (void *) -1; + } + + /* if no client is connected then make the new client available for connection */ + if (r.client->socket == -1) + { + memcpy(r.new_client, &temp_client, sizeof(client_t)); + return (void *) 0; + } + + /* if there is already a new client being handled then reject this client request */ + if (r.new_client->socket != -1) + { + sprintf(msg, "\nToo many connection requests, please try later.\n"); + send(temp_client.socket, msg, strlen(msg), 0); + client_close(&temp_client); + time2string(time(NULL), timestamp); + fprintf(stderr, "[%s] rejected new client request %s @ %s\n", __func__, + temp_client.ip_string, timestamp); + return (void *) 0; + } + + /* make the new client available for connection */ + memcpy(r.new_client, &temp_client, sizeof(client_t)); + + /* inform the new client that the port is already in use */ + time2string(r.client->last_active, timestamp); + sprintf(msg, "\nPort %u is already being used!\nCurrent user and last activity:\n%s @ %s\n", + r.server->port, r.client->username, timestamp); + send(r.new_client->socket, msg, strlen(msg), 0); + + /* ask the new client if the currently connected client should be dropped */ + sprintf(msg, "\nDo you want to drop the current user?\n" + "If yes then please type YES DROP (in uppercase):\n"); + send(r.new_client->socket, msg, strlen(msg), 0); + + /* wait for client input */ + client_wait_line(r.new_client); + + /* check client confirmation */ + if (strncmp(r.new_client->data, "YES DROP", 8) == 0) + { + /* drop the connected client */ + client_close(r.client); + fprintf(stderr, "[%s] dropped client %s @ %s\n", __func__, + r.client->ip_string, timestamp); + } + else + { + /* reject this client request */ + client_close(r.new_client); + time2string(time(NULL), timestamp); + fprintf(stderr, "[%s] rejected new client request %s @ %s\n", __func__, + temp_client.ip_string, timestamp); + return (void *) 1; + } + + return (void *) 0; +} + +void* thread_tty_data(void *args) +{ + struct timeval tv; + ssize_t br = 0; + int ret; + fd_set read_fds; + + /* get resources from args */ + resources_t r = *((resources_t*) args); + + fprintf(stderr, "[%s] tty thread started with passed argument: %s\n", + NAME, r.tty_dev->path); + + while (1) + { + /* setup parameters for select() */ + tv.tv_sec = TTY_WAIT_TIMEOUT; + tv.tv_usec = 0; + FD_ZERO(&read_fds); + FD_SET(r.tty_dev->fd, &read_fds); + + /* wait with select() */ + ret = select(r.tty_dev->fd + 1, &read_fds, NULL, NULL, &tv); + + if (ret > 0 && FD_ISSET(r.tty_dev->fd, &read_fds)) + { + br = tty_read(r.tty_dev); + client_write(r.client, r.tty_dev->data, br); + } + else + { + + } + + if (debug_messages) + { + fprintf(stderr, "[%s] tty thread alive\n", NAME); + } + } + + fprintf(stderr, "[%s] tty thread stoped\n", NAME); + + return (void*) 0; +} + +void* thread_client_data(void *args) +{ + int ret; + + fd_set read_fds; + int fdmax; + struct timeval tv; + + pthread_t new_client_thread; + + /* loop with timeouts waiting for client connection requests and data */ + while (1) { + + /* check if new client is available for connection */ + if ( (client.socket == -1) && (new_client.socket != -1) ) { + /* copy new client information */ + memcpy(&client, &new_client, sizeof(client_t)); + new_client.socket = -1; + fprintf(stderr, "[%s] client %s connected\n", NAME, client.ip_string); + /* ask client to provide a username before going to "character" mode */ + if (client_ask_username(&client) != 0) { + /* close client if not able to provide a username */ + client_close(&client); + continue; + } + /* put client in "character" mode */ + char msg[TELNET_MSG_SIZE_CHARMODE]; + telnet_message_set_character_mode(msg); + client_write(&client, msg, TELNET_MSG_SIZE_CHARMODE); + } + + /* setup parameters for select() */ + tv.tv_sec = SERVER_WAIT_TIMEOUT; + tv.tv_usec = 0; + FD_ZERO(&read_fds); + FD_SET(server.socket, &read_fds); + if (client.socket != -1) { + FD_SET(client.socket, &read_fds); /* wait for client if connected */ + } + fdmax = (server.socket > client.socket) ? server.socket : client.socket; + + /* wait with select() */ + ret = select(fdmax+1, &read_fds, NULL, NULL, &tv); + if (ret == -1) { + //TODO do we really break here and stop server when select returns an error? + fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); + break; + } + if (ret > 0) { + /* check server status */ + if (FD_ISSET(server.socket, &read_fds)) { + fprintf(stderr, "[%s] received client connection request\n", NAME); + /* handle new client connection request in a separate thread */ + resources_t r = {&server, &client, &new_client, &tty_dev}; + if (pthread_create(&new_client_thread, NULL, thread_new_client_connection, &r) != 0) { + /* print error but continue waiting for connection request */ + fprintf(stderr, "[%s] problem with handling client connection request\n", NAME); + continue; + } + } + /* check client status if connected */ + if ( (client.socket != -1) && FD_ISSET(client.socket, &read_fds) ) { + /* read client data */ + ret = client_read(&client); + /* check if client disconnected or other errors occurred */ + if (ret == -ENODATA) { + fprintf(stderr, "[%s] client %s disconnected\n", NAME, client.ip_string); + /* close client connection and continue waiting for new clients */ + client_close(&client); + continue; + } + if ( ret < 0) { + /* print error but continue waiting for new data */ + fprintf(stderr, "[%s] problem reading client\n", NAME); + continue; + } + /* pass received data to tty device */ + if (tty_dev.fd != -1) { + tty_write(&tty_dev, client.data, ret); + } + } + } + if (ret == 0) { + /* do something with inactive client */ + if (client.socket != -1) { + //TODO we could drop client if inactive for some time + time_t current_time = time(NULL); + if (debug_messages) { + fprintf(stderr, "[%s] client last active %u seconds ago\n", NAME, + (unsigned int) (current_time - client.last_active)); + } + } + /* do something while listening for client connections */ + else { + if (debug_messages) { + fprintf(stderr, "[%s] listening for client connection\n", NAME); + } + } + } + + } /* END while() loop */ + + return (void*) 0; +} + /* MoxaNix main program loop. */ int main(int argc, char *argv[]) { int ret; unsigned int tcp_port = -1; int def_conf = 0; // is default config used or from .cfg file - - fd_set read_fds; - int fdmax; - struct timeval tv; - pthread_t tty_thread, new_client_thread; + pthread_t tty_thread; /* zero init tty_dev */ if (cfsetispeed(&(tty_dev.ttyset), B0) < 0 || @@ -170,103 +385,15 @@ int main(int argc, char *argv[]) { fprintf(stderr, "[%s] TCP port: %d, TTY device path: %s\n", NAME, tcp_port, tty_dev.path); /* start thread that handles tty device */ - ret = pthread_create(&tty_thread, NULL, tty_thread_func, &tty_dev); + resources_t r = {&server, &client, &new_client, &tty_dev}; + ret = pthread_create(&tty_thread, NULL, thread_tty_data, &r); if (ret) { fprintf(stderr, "[%s] error starting serial monitor thread" ", pthread_create returned %d\n", NAME, ret); return -1; } - /* loop with timeouts waiting for client connection requests and data */ - while (1) { - - /* check if new client is availabe for connection */ - if ( (client.socket == -1) && (new_client.socket != -1) ) { - /* copy new client information */ - memcpy(&client, &new_client, sizeof(client_t)); - new_client.socket = -1; - fprintf(stderr, "[%s] client %s connected\n", NAME, client.ip_string); - /* ask client to provide a username before going to "character" mode */ - if (client_ask_username(&client) != 0) { - /* close client if not able to provide a username */ - client_close(&client); - continue; - } - /* put client in "character" mode */ - char msg[TELNET_MSG_SIZE_CHARMODE]; - telnet_message_set_character_mode(msg); - client_write(&client, msg, TELNET_MSG_SIZE_CHARMODE); - } - - /* setup parameters for select() */ - tv.tv_sec = SERVER_WAIT_TIMEOUT; - tv.tv_usec = 0; - FD_ZERO(&read_fds); - FD_SET(server.socket, &read_fds); - if (client.socket != -1) { - FD_SET(client.socket, &read_fds); /* wait for client if connected */ - } - fdmax = (server.socket > client.socket) ? server.socket : client.socket; - - /* wait with select() */ - ret = select(fdmax+1, &read_fds, NULL, NULL, &tv); - if (ret == -1) { - //TODO do we really break here and stop server when select returns an error? - fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); - break; - } - if (ret > 0) { - /* check server status */ - if (FD_ISSET(server.socket, &read_fds)) { - fprintf(stderr, "[%s] received client connection request\n", NAME); - /* handle new client connection request in a separate thread */ - if (pthread_create(&new_client_thread, NULL, server_new_client_thread, NULL) != 0) { - /* print error but continue waiting for connection request */ - fprintf(stderr, "[%s] problem with handling client connection request\n", NAME); - continue; - } - } - /* check client status if connected */ - if ( (client.socket != -1) && FD_ISSET(client.socket, &read_fds) ) { - /* read client data */ - ret = client_read(&client); - /* check if client disconnected or other errors occurred */ - if (ret == -ENODATA) { - fprintf(stderr, "[%s] client %s disconnected\n", NAME, client.ip_string); - /* close client connection and continue waiting for new clients */ - client_close(&client); - continue; - } - if ( ret < 0) { - /* print error but continue waiting for new data */ - fprintf(stderr, "[%s] problem reading client\n", NAME); - continue; - } - /* pass received data to tty device */ - if (tty_dev.fd != -1) { - tty_write(&tty_dev, client.data, ret); - } - } - } - if (ret == 0) { - /* do something with inactive client */ - if (client.socket != -1) { - //TODO we could drop client if inactive for some time - time_t current_time = time(NULL); - if (debug_messages) { - fprintf(stderr, "[%s] client last active %u seconds ago\n", NAME, - (unsigned int) (current_time - client.last_active)); - } - } - /* do something while listening for client connections */ - else { - if (debug_messages) { - fprintf(stderr, "[%s] listening for client connection\n", NAME); - } - } - } - - } /* END while() loop */ + thread_client_data(NULL); /* unexpected break from while() loop */ fprintf(stderr, "[%s] unexpected condition\n", NAME); diff --git a/moxerver.h b/moxerver.h index 4d0b4ce..617f0d0 100644 --- a/moxerver.h +++ b/moxerver.h @@ -1,140 +1,63 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* TCP_NODELAY */ -#include -#include -#include +#pragma once +#include #include +#include +#include +#include -#define DATABUF_LEN 128 -#define DEV_PATH 128 -#define TIMESTAMP_FORMAT "%Y-%m-%dT%H:%M:%S" /* ISO 8601 */ -#define TIMESTAMP_LEN 20+1 /* calculated following the timestamp format */ #define CONFILE "moxanix.cfg" -#define USERNAME_LEN 32 - -/* Structures used for communication parameters. */ -typedef struct -{ - int socket; /* server socket */ - struct sockaddr_in address; /* server address information */ - unsigned int port; /* server port in host byte order, practical reference */ -} server_t; - -typedef struct -{ - int socket; /* client socket */ - struct sockaddr_in address; /* client address information */ - char ip_string[INET_ADDRSTRLEN]; /* client IP address as a string */ - time_t last_active; /* time of client's last activity in seconds from Epoch */ - char username[USERNAME_LEN]; /* username for human identification */ - char data[DATABUF_LEN]; /* buffer for data received from client */ -} client_t; - -typedef struct -{ - int fd; /* tty file descriptor */ - struct termios ttysetdef; /* default tty termios settings */ - struct termios ttyset; /* tty termios settings */ - char path[DEV_PATH]; /* tty device path */ - char data[DATABUF_LEN]; /* buffer for data received from tty */ -} tty_t; +#define SERVER_WAIT_TIMEOUT 2 /* seconds for select() timeout in server loop */ +#define TTY_WAIT_TIMEOUT 5 /* seconds for select() timeout in server loop */ /* Global variables used throughout the application. */ -int debug_messages; /* if > 0 debug messages will be printed */ server_t server; /* main server structure */ client_t client; /* connected client structure */ //TODO working with only 1 client, this can be expanded into a list client_t new_client; /* client structure for new client request */ tty_t tty_dev; /* connected tty device */ - -/* Global functions used throughout the application. */ +typedef struct +{ + server_t *server; + client_t *client; + client_t *new_client; + tty_t *tty_dev; +} resources_t; /** - * Converts from time in seconds from Epoch to conveniently formatted string. - * - * Returns: - * 0 always - */ -int time2string(time_t time, char* timestamp); - - -/* Functions handling server operation. */ - -/** - * Sets up the server on specific port, binds to a socket and listens for client connections. + * The thread function handling new client connections. + * + * If there is no connected clients then the first client request is accepted. + * If there is a connected client then the new client is asked if the currently + * connected client should be dropped. + * + * The function handles global resources through the pointer to a "resources_t" + * structure passed as the input argument. * * Returns: - * - 0 on success, - * - negative errno value set appropriately by error in setup process + * Return value from this thread function is not used. */ -int server_setup(server_t *server, unsigned int port); +void* thread_new_client_connection(void *args); /** - * Closes the server socket. - * - * Returns: - * 0 always, but internally tries closing again if it fails - */ -int server_close(server_t *server); - -/** - * Accepts incoming client connection. - * - * Returns: - * - 0 on success, - * - negative errno value set appropriately by error in setup process - */ -int server_accept(server_t *server, client_t *accepted_client); - -/** - * Thread function handling new client connections. - * If there is no connected client then first client request is accepted. - * If there is a connected client then new client is asked if connected client should be dropped. - * + * The thread function handling data from the tty device. + * + * The function handles global resources through the pointer to a "resources_t" + * structure passed as the input argument. + * * Returns: * Return value from this thread function is not used. - * Function handles global client variables: - * - client structure is reset if new client is available to connect to - * - new_client structure stores information about the client to connect to */ -void* server_new_client_thread(void *args); - - - - - - - - -/* Functions handling communication with tty device. */ - -/* Opens the tty device and configures it. */ -int tty_open(tty_t *tty_dev); - -/* Closes the tty device. */ -int tty_close(tty_t *tty_dev); - -/* Reconfigures the tty device. */ -int tty_reconfigure(tty_t *tty_dev, struct termios newttyset); - -/* Reads incoming data from tty device to tty data buffer. */ -int tty_read(tty_t *tty_dev); - -/* Sends data from a buffer to tty device. */ -int tty_write(tty_t *tty_dev, char *databuf, int datalen); - -/* Main tty thread function */ -void *tty_thread_func(void *arg); -int speed_to_baud(speed_t speed); -speed_t baud_to_speed(int baud); +void* thread_tty_data(void *args); +/** + * The thread function handling data from the connected client. + * + * The function handles global resources through the pointer to a "resources_t" + * structure passed as the input argument. + * + * Returns: + * Return value from this thread function is not used. + */ +void* thread_client_data(void *args); diff --git a/server.c b/server.c index b9ab5de..2f684b7 100644 --- a/server.c +++ b/server.c @@ -1,12 +1,9 @@ -/* - * Handling server operation. - */ +#include +#include /* TCP_NODELAY */ +#include -#include "moxerver.h" - -/* Sets up the server on specific port, binds to a socket and listens for client connections. */ -int server_setup(server_t *server, unsigned int port) { - +int server_setup(server_t *server, unsigned int port) +{ int opt; char timestamp[TIMESTAMP_LEN]; @@ -17,28 +14,36 @@ int server_setup(server_t *server, unsigned int port) { /* create stream socket using TCP */ server->socket = socket(AF_INET, SOCK_STREAM, 0); - if (server->socket == -1) { - fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); + if (server->socket == -1) + { + fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, + errno, strerror(errno)); return -errno; } fprintf(stderr,"[%s] socket created\n", __func__); /* try to avoid "Address already in use" error */ opt = 1; /* true value for setsockopt option */ - if (setsockopt(server->socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) == -1) { - fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); + if (setsockopt(server->socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) == -1) + { + fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, + errno, strerror(errno)); return -errno; } /* turn off Nagle algorithm */ opt = 1; /* true value for setsockopt option */ - if (setsockopt(server->socket, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)) == -1) { - fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); + if (setsockopt(server->socket, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)) == -1) + { + fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, + errno, strerror(errno)); return -errno; } /* bind server address to a socket */ - if (bind(server->socket, (struct sockaddr *) &server->address, sizeof(server->address)) == -1) { - fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); + if (bind(server->socket, (struct sockaddr *) &server->address, sizeof(server->address)) == -1) + { + fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, + errno, strerror(errno)); return -errno; } fprintf(stderr,"[%s] bind successful\n", __func__); @@ -48,8 +53,10 @@ int server_setup(server_t *server, unsigned int port) { fprintf(stderr,"[%s] assigned port %u\n", __func__, server->port); //ntohs(server->address.sin_port) /* listen for a client connection, allow some connections in queue */ - if (listen(server->socket, 1) == -1) { - fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); + if (listen(server->socket, 1) == -1) + { + fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, + errno, strerror(errno)); return -errno; } @@ -58,42 +65,46 @@ int server_setup(server_t *server, unsigned int port) { return 0; } -/* Closes the server socket. */ -int server_close(server_t *server) { +void server_close(server_t *server) +{ char timestamp[TIMESTAMP_LEN]; + /* force closing in case of error */ - if (close(server->socket) == -1) { + if (close(server->socket) == -1) + { close(server->socket); } + time2string(time(NULL), timestamp); fprintf(stderr,"[%s] socket closed, server is down @ %s\n", __func__, timestamp); - return 0; } -/* Accepts incoming client connection. */ -int server_accept(server_t *server, client_t *accepted_client) { - +int server_accept(server_t *server, client_t *accepted_client) +{ int namelen; char timestamp[TIMESTAMP_LEN]; /* accept connection request */ namelen = sizeof(accepted_client->address); accepted_client->socket = accept(server->socket, (struct sockaddr *) &accepted_client->address, (socklen_t *) &namelen); - if (accepted_client->socket == -1) { + if (accepted_client->socket == -1) + { fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); return -errno; } - /* make client socket non-blocking */ - if (fcntl(accepted_client->socket, F_SETFL, O_NONBLOCK) == -1) { + /* make the client socket non-blocking */ + if (fcntl(accepted_client->socket, F_SETFL, O_NONBLOCK) == -1) + { fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); return -errno; } - /* get client IP address as human readable string */ - inet_ntop(accepted_client->address.sin_family, &accepted_client->address.sin_addr.s_addr, + /* get client IP address as human a readable string */ + inet_ntop(accepted_client->address.sin_family, + &accepted_client->address.sin_addr.s_addr, accepted_client->ip_string, INET_ADDRSTRLEN); - /* grab current time and store it as client last activity*/ + /* grab current time and store it as client's last activity*/ accepted_client->last_active = time(NULL); /* print client information */ @@ -102,65 +113,3 @@ int server_accept(server_t *server, client_t *accepted_client) { __func__, accepted_client->ip_string, timestamp); return 0; } - -/* Thread function handling new client connections. Handles global client variables. */ -void* server_new_client_thread(void *args) { - - char msg[DATABUF_LEN]; - char timestamp[TIMESTAMP_LEN]; - client_t temp_client; - - /* accept new connection request */ - if (server_accept(&server, &temp_client) != 0) return (void *) -1; - - /* if no client is connected then make new client available for connection */ - if (client.socket == -1) { - memcpy(&new_client, &temp_client, sizeof(client_t)); - return (void *) 0; - } - - /* if there is already a new client being handled then reject this client request */ - if (new_client.socket != -1) { - sprintf(msg, "\nToo many connection requests, please try later.\n"); - send(temp_client.socket, msg, strlen(msg), 0); - client_close(&temp_client); - time2string(time(NULL), timestamp); - fprintf(stderr, "[%s] rejected new client request %s @ %s\n", - __func__, temp_client.ip_string, timestamp); - return (void *) 0; - } - - /* make new client available for connection */ - memcpy(&new_client, &temp_client, sizeof(client_t)); - - /* inform new client that port is already in use */ - time2string(client.last_active, timestamp); - sprintf(msg, "\nPort %u is already being used!\nCurrent user and last activity:\n%s @ %s\n", - server.port, client.username, timestamp); - send(new_client.socket, msg, strlen(msg), 0); - - /* ask new client if current client should be dropped */ - sprintf(msg, "\nDo you want to drop the current user?\nIf yes then please type YES DROP (in uppercase):\n"); - send(new_client.socket, msg, strlen(msg), 0); - - /* wait for client input */ - client_wait_line(&new_client); - - /* check client confirmation */ - if (strncmp(new_client.data, "YES DROP", 8) == 0) { - /* drop connected client */ - client_close(&client); - fprintf(stderr, "[%s] dropped client %s @ %s\n", - __func__, client.ip_string, timestamp); - } - else { - /* reject this client request */ - client_close(&new_client); - time2string(time(NULL), timestamp); - fprintf(stderr, "[%s] rejected new client request %s @ %s\n", - __func__, temp_client.ip_string, timestamp); - return (void *) 1; - } - - return (void *) 0; -} diff --git a/server.h b/server.h new file mode 100644 index 0000000..6132fbc --- /dev/null +++ b/server.h @@ -0,0 +1,37 @@ +#pragma once + +/* Handles server operation. */ + +#include +#include + +typedef struct +{ + int socket; /* server socket */ + struct sockaddr_in address; /* server address information */ + unsigned int port; /* server port in host byte order, practical reference */ +} server_t; + +/** + * Sets up the server on a specific port, binds to a socket and listens for + * client connections. + * + * Returns: + * - 0 on success, + * - negative errno value set by an error in the setup process + */ +int server_setup(server_t *server, unsigned int port); + +/** + * Closes the server socket. + */ +void server_close(server_t *server); + +/** + * Accepts an incoming client connection. + * + * Returns: + * - 0 on success, + * - negative errno value set by an error in the process + */ +int server_accept(server_t *server, client_t *accepted_client); diff --git a/telnet.c b/telnet.c index 99c4e3c..138ab12 100644 --- a/telnet.c +++ b/telnet.c @@ -1,5 +1,4 @@ #include -#include /* structure for holding telnet option name and value */ typedef struct diff --git a/telnet.h b/telnet.h index d864120..9deb47b 100644 --- a/telnet.h +++ b/telnet.h @@ -1,5 +1,9 @@ +#pragma once + /* Handles details related to telnet protocol. */ +#include + #define TELNET_MSG_SIZE_CHARMODE 9 /** diff --git a/tty.c b/tty.c index 1f4fe89..3ed6a70 100644 --- a/tty.c +++ b/tty.c @@ -1,16 +1,10 @@ -/* - * Handling communication with tty device. - */ +#include -#include "moxerver.h" - -#define TTY_THREAD_TIMEOUT_SEC 30 -#define TTY_WAIT_TIMEOUT 5 /* seconds for select() timeout in server loop */ #define NAME "tty" #define TTY_DEF_BAUD_RATE B115200 -/* Opens the tty device and configures it. */ -int tty_open(tty_t *tty_dev) { +int tty_open(tty_t *tty_dev) +{ int fd; // PROPOSAL: // open tty device to get file descriptor @tty_dev.fd @@ -18,14 +12,20 @@ int tty_open(tty_t *tty_dev) { // apply settings by calling tcsetattr(fd, ttyset) // on success copy path to @tty_dev.path if ((fd = open (tty_dev->path, O_RDWR | O_NOCTTY | O_SYNC)) < 0) + { return -errno; - else + } + else + { tty_dev->fd = fd; + } /* store default termios setitngs */ if (tcgetattr(tty_dev->fd, &(tty_dev->ttysetdef))) + { fprintf(stderr, "[%s] error reading device default config\n" "\t\t-> default config will not be restored upon exit", __func__); + } tty_dev->ttyset.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON); @@ -39,112 +39,114 @@ int tty_open(tty_t *tty_dev) { /* if speed is set to B0 (e.g. cfg file is not provided), default values are used */ if (cfgetispeed(&(tty_dev->ttyset)) == baud_to_speed(0) && - cfsetispeed(&(tty_dev->ttyset), TTY_DEF_BAUD_RATE) < 0) { + cfsetispeed(&(tty_dev->ttyset), TTY_DEF_BAUD_RATE) < 0) + { fprintf(stderr, "[%s] error configuring tty device speed\n", __func__); return -errno; } if (cfgetospeed(&(tty_dev->ttyset)) == baud_to_speed(0) && - cfsetospeed(&(tty_dev->ttyset), TTY_DEF_BAUD_RATE) < 0) { + cfsetospeed(&(tty_dev->ttyset), TTY_DEF_BAUD_RATE) < 0) + { fprintf(stderr, "[%s] error configuring tty device speed\n", __func__); return -errno; } - if (tcsetattr(tty_dev->fd, TCSANOW, &(tty_dev->ttyset)) < 0) { + if (tcsetattr(tty_dev->fd, TCSANOW, &(tty_dev->ttyset)) < 0) + { fprintf(stderr, "[%s] error configuring tty device\n", __func__); return -errno; } return 0; - } -/* Closes the tty device. */ -int tty_close(tty_t *tty_dev) { - +int tty_close(tty_t *tty_dev) +{ int fd = tty_dev->fd; tty_dev->fd = -1; fprintf(stderr, "[%s] closing tty device \n", __func__); - if (tcsetattr(fd, TCSANOW, &(tty_dev->ttysetdef)) < 0) { + if (tcsetattr(fd, TCSANOW, &(tty_dev->ttysetdef)) < 0) + { fprintf(stderr, "[%s] error restoring tty device default config\n", __func__); return -errno; } if (close(fd) < 0) + { return -errno; + } return 0; } -/* Reconfigures the tty device. */ -int tty_reconfigure(tty_t *tty_dev, struct termios newttyset) { +int tty_reconfigure(tty_t *tty_dev, struct termios newttyset) +{ // not sure how to organize this: // 1. parameters in external termios struct, copied @tty_dev.ttyset, applied with tcsetattr() // 2. parameters directly @tty_dev.ttyset, applied with tcsetattr() return 0; } -/* Reads incoming data from tty device to tty data buffer. */ -int tty_read(tty_t *tty_dev) { - // read and save @tty_dev.data - return 0; -} +int tty_read(tty_t *tty_dev) +{ + int len; -/* Sends data from a buffer to tty device. */ -int tty_write(tty_t *tty_dev, char *databuf, int datalen) { - write(tty_dev->fd, databuf, datalen); - // databuf should point to client data buffer - return 0; -} - -/* Main thread for reading and writing to tty device */ -void *tty_thread_func(void *arg) { - //char c; - tty_t *tty_dev = (tty_t*)arg; - struct timeval tv; - ssize_t br = 0; - int ret; - fd_set read_fds; - - fprintf(stderr, "[%s] tty thread started with passed argument: %s\n", NAME, tty_dev->path); - - while (1) { - /* setup parameters for select() */ - tv.tv_sec = TTY_WAIT_TIMEOUT; - tv.tv_usec = 0; - FD_ZERO(&read_fds); - FD_SET(tty_dev->fd, &read_fds); - - /* wait with select() */ - ret = select(tty_dev->fd + 1, &read_fds, NULL, NULL, &tv); - - if (ret > 0 && FD_ISSET(tty_dev->fd, &read_fds)) { - br = read(tty_dev->fd, tty_dev->data, DATABUF_LEN); - client_write(&client, tty_dev->data, br); - } - else { - } - //sleep(10); - //if (read(tty_dev->fd, &c, 1) > 0) - // printf("%c", c); - - //fprintf(stderr, "[%s] tty thread reporting ...\n", NAME); - //i++; + len = read(tty_dev->fd, tty_dev->data, DATABUF_LEN); + if (len == -1) + { + fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, + errno, strerror(errno)); + return -errno; } - fprintf(stderr, "[%s] tty thread stoped\n", NAME); + //TODO let's print received bytes during development phase... + if (debug_messages) + { + int i; + for(i = 0; i < len; i++) + { + fprintf(stderr, "tty <- %u '%c'\n", + (unsigned char) tty_dev->data[i], + (unsigned char) tty_dev->data[i]); + } + } - return (void *)tty_dev;; + return len; +} + +int tty_write(tty_t *tty_dev, char *databuf, int datalen) +{ + int len; + + len = write(tty_dev->fd, databuf, datalen); + if (len == -1) + { + fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, + errno, strerror(errno)); + return -errno; + } + + //TODO let's print received bytes during development phase... + if (debug_messages) + { + int i; + for(i = 0; i < datalen; i++) + { + fprintf(stderr, "tty -> %u '%c'\n", + (unsigned char) databuf[i], + (unsigned char) databuf[i]); + } + } + + return len; } -/* - * Converts POSIX speed_t to a baud rate. The values of the - * constants for speed_t are not themselves portable. - */ int speed_to_baud(speed_t speed) { - switch (speed) { + switch (speed) + { case B0: return 0; case B50: @@ -186,12 +188,10 @@ int speed_to_baud(speed_t speed) } } -/* - * Converts a numeric baud rate to a POSIX speed_t. - */ speed_t baud_to_speed(int baud) { - switch (baud) { + switch (baud) + { case 0: return B0; case 50: diff --git a/tty.h b/tty.h new file mode 100644 index 0000000..e9e6e43 --- /dev/null +++ b/tty.h @@ -0,0 +1,62 @@ +#pragma once + +/* Handles communication with a tty device. */ + +#include +#include +#include + +#define DEV_PATH 128 + +typedef struct +{ + int fd; /* tty file descriptor */ + struct termios ttysetdef; /* default tty termios settings */ + struct termios ttyset; /* tty termios settings */ + char path[DEV_PATH]; /* tty device path */ + char data[DATABUF_LEN]; /* buffer for data received from tty */ +} tty_t; + +/** + * Opens the tty device and configures it. + */ +int tty_open(tty_t *tty_dev); + +/** + * Closes the tty device. + */ +int tty_close(tty_t *tty_dev); + +/** + * Reconfigures the tty device. + */ +int tty_reconfigure(tty_t *tty_dev, struct termios newttyset); + +/** + * Reads incoming data from tty device to tty data buffer. + * + * Returns: + * - number of read bytes on success, + * - negative errno value set by an error while readin + */ +int tty_read(tty_t *tty_dev); + +/** + * Sends data from a buffer to tty device. + * + * Returns: + * - number of sent bytes on success, + * - negative errno value set by an error while sending + */ +int tty_write(tty_t *tty_dev, char *databuf, int datalen); + +/** + * Converts POSIX speed_t to a baud rate. The values of the + * constants for speed_t are not themselves portable. + */ +int speed_to_baud(speed_t speed); + +/** + * Converts a numeric baud rate to a POSIX speed_t. + */ +speed_t baud_to_speed(int baud);