More extraction of functionality

Extracting server and tty functions into separate headers.
Thread functions for new connections, tty data and client data
are also extracted and used as separate functional blocks.
This commit is contained in:
Igor Socec 2016-11-19 00:12:08 +01:00
parent 2c26ef201a
commit c17e696566
11 changed files with 534 additions and 393 deletions

View file

@ -1,4 +1,5 @@
#include <client.h>
#include <telnet.h>
void client_close(client_t *client)
{

View file

@ -1,6 +1,21 @@
/* Handles communication with clients. */
#pragma once
#include <moxerver.h>
/* Handles communication with a client. */
#include <common.h>
#include <netinet/in.h>
#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.

24
common.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#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);

View file

@ -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);

View file

@ -1,140 +1,63 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h> /* TCP_NODELAY */
#include <arpa/inet.h>
#include <termios.h>
#include <time.h>
#pragma once
#include <common.h>
#include <telnet.h>
#include <client.h>
#include <server.h>
#include <tty.h>
#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);

135
server.c
View file

@ -1,12 +1,9 @@
/*
* Handling server operation.
*/
#include <server.h>
#include <netinet/tcp.h> /* TCP_NODELAY */
#include <arpa/inet.h>
#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;
}

37
server.h Normal file
View file

@ -0,0 +1,37 @@
#pragma once
/* Handles server operation. */
#include <common.h>
#include <client.h>
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);

View file

@ -1,5 +1,4 @@
#include <telnet.h>
#include <moxerver.h>
/* structure for holding telnet option name and value */
typedef struct

View file

@ -1,5 +1,9 @@
#pragma once
/* Handles details related to telnet protocol. */
#include <common.h>
#define TELNET_MSG_SIZE_CHARMODE 9
/**

152
tty.c
View file

@ -1,16 +1,10 @@
/*
* Handling communication with tty device.
*/
#include <tty.h>
#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:

62
tty.h Normal file
View file

@ -0,0 +1,62 @@
#pragma once
/* Handles communication with a tty device. */
#include <common.h>
#include <client.h>
#include <termios.h>
#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);