diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..7c8ca4a --- /dev/null +++ b/build.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Builds MoxaNix project + +#TODO switch to Makefile :) + +name="moxerver" + +include_dir="." +src_list="$name.c server.c client.c tty.c" + +gcc $src_list -o $name -I $include_dir diff --git a/client.c b/client.c new file mode 100644 index 0000000..661e67b --- /dev/null +++ b/client.c @@ -0,0 +1,48 @@ +#include "moxerver_include.h" + + +/* Closes client connection. */ +int client_close(struct client_t *client) { + /* force closing in case of error */ + if (close(client->socket) == -1) { + close(client->socket); + } + client->socket = -1; + fprintf(stderr,"[%s]: socket closed for client %s\n", __func__, client->ip_string); + return 0; +} + +/* Reads incoming data from client to client data buffer. */ +int client_read(struct client_t *client) { + + int len; + + /* read client data */ + len = recv(client->socket, client->data, sizeof(client->data)-1, 0); + if (len == -1) { + fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); + return -errno; + } + /* null-terminate received data */ + client->data[len] = '\0'; + + //TODO how does a client disconnect? For now let's wait for QUIT command... + if (!strncmp(client->data, "QUIT", 4)) { + client_close(client); + } + + //TODO let's print the data during development phase... + fprintf(stderr, "client %s says: %s", client->ip_string, client->data); + + return 0; +} + +/* Sends data from a buffer to client. */ +int client_write(struct client_t *client, char *databuf, int datalen) { + /* send data to client */ + if (send(client->socket, databuf, datalen, 0) == -1) { + fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); + return -errno; + } + return 0; +} diff --git a/moxerver b/moxerver new file mode 100755 index 0000000..7898d6d Binary files /dev/null and b/moxerver differ diff --git a/moxerver.c b/moxerver.c new file mode 100644 index 0000000..aaf0c15 --- /dev/null +++ b/moxerver.c @@ -0,0 +1,120 @@ +#include "moxerver_include.h" +#include /* getopt() */ + + +#define SERVER_WAIT_TIMEOUT 5 /* seconds for select() timeout in server loop */ + +/* Prints help message. */ +void usage() { + //TODO maybe some styling should be done + fprintf(stderr, "Usage: moxerver -p port [-h]\n\n"); +} + +/* MoxaNix main program loop. */ +int main(int argc, char *argv[]) { + + int ret; + + struct server_t server; + struct client_t client; //TODO working with only 1 client, this can be expanded into a list + unsigned int port; + + fd_set read_fds; + int fdmax; + struct timeval tv; + + + /* grab argumments */ + while ((ret = getopt(argc, argv, ":p:h")) != -1) { + switch (ret) { + /* get server port number */ + case 'p': + port = (unsigned int) atoi(optarg); + /* check port range */ + if (port < 4001 || port > 4008) { + fprintf(stderr, "error: port number out of 4001-4008 range\n"); + return -1; + } + break; + /* print help and exit */ + case 'h': + usage(); + return 0; + default: + fprintf(stderr, "error parsing arguments\n"); + usage(); + return 0; + } + } + + /* introduction message */ + fprintf(stderr, "=== MoxaNix ===\n"); + + /* initialize */ + server_setup(&server, port); + client.socket = -1; + + //TODO this is a good place to create and start the TTY thread + + /* loop with timeouts waiting for client connection */ + while (1) { + + /* 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 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, "received client connection request\n"); + /* accept connection request if there is no client connected yet */ + if (client.socket == -1) { + ret = server_accept(&server, &client); + if ( ret != 0) { + /* print error but continue waiting for connection request */ + fprintf(stderr, "problem accepting client\n"); + continue; + } + } + /* reject connection request if a client is already connected */ + else { + server_reject(&server); + } + } + /* check client status if connected */ + if ( (client.socket != -1) && FD_ISSET(client.socket, &read_fds) ) { + /* read client data */ + ret = client_read(&client); + if ( ret != 0) { + /* print error but continue waiting for new data */ + fprintf(stderr, "problem reading client\n"); + continue; + } + //TODO we should send this data to TTY device + } + } + if (ret == 0) { + fprintf(stderr, "server waiting\n"); + } + + } /* END while loop */ + + /* close server and client */ + server_close(&server); + client_close(&client); + + return 0; +} \ No newline at end of file diff --git a/moxerver_include.h b/moxerver_include.h new file mode 100644 index 0000000..e79f63d --- /dev/null +++ b/moxerver_include.h @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DATA_BUFLEN 128 + + +struct server_t { + int socket; /* server socket */ + struct sockaddr_in address; /* server address information */ + unsigned int port; /* server port in host byte order, practical reference */ +}; + +struct client_t { + int socket; /* client socket */ + struct sockaddr_in address; /* client address information */ + char ip_string[INET_ADDRSTRLEN]; /* client IP address as a string */ + char data[DATA_BUFLEN]; /* buffer for data received from client */ +}; + +struct tty_t { + int fd; /* tty file descriptor */ + struct termios ttyset; /* tty termios settings */ + char data[DATA_BUFLEN]; /* buffer for data received from tty */ +}; + + +/* Sets up the server on specific port, binds to a socket and listens for client connections. */ +int server_setup(struct server_t *server, unsigned int port); +/* Closes the server. */ +int server_close(struct server_t *server); +/* Accepts incoming client connection. */ +int server_accept(struct server_t *server, struct client_t *accepted_client); +/* Rejects incoming client connection. */ +int server_reject(struct server_t *server); + + +/* Closes client connection. */ +int client_close(struct client_t *client); +/* Reads incoming data from client to client data buffer. */ +int client_read(struct client_t *client); +/* Sends data from a buffer to client. */ +int client_write(struct client_t *client, char *databuf, int datalen); + + +/* Opens the tty device and configures it. */ +int tty_open(struct tty_t *tty_dev); +/* Closes the tty device. */ +int tty_close(struct tty_t *tty_dev); +/* Reconfigures the tty device. */ +int tty_reconfigure(struct tty_t *tty_dev, struct termios newttyset); +/* Reads incoming data from tty device to tty data buffer. */ +int tty_read(struct tty_t *tty_dev); +/* Sends data from a buffer to tty device. */ +int tty_write(struct tty_t *tty_dev, char *databuf, int datalen); diff --git a/server.c b/server.c new file mode 100644 index 0000000..a8fb8b8 --- /dev/null +++ b/server.c @@ -0,0 +1,112 @@ +#include "moxerver_include.h" + + +/* Sets up the server on specific port, binds to a socket and listens for client connections. */ +int server_setup(struct server_t *server, unsigned int port) { + + int namelen, opt; + + /* set up server address information */ + server->address.sin_family = AF_INET; /* use IPv4 address family */ + server->address.sin_port = htons(port); /* set up port number, htons is for using network byte order */ + server->address.sin_addr.s_addr = INADDR_ANY; /* use local address */ + + /* 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)); + 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)); + 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)); + return -errno; + } + fprintf(stderr,"[%s]: bind successful\n", __func__); + + /* check port assignment */ + namelen = sizeof(server->address); + if (getsockname(server->socket, (struct sockaddr *) &server->address, &namelen) == -1) { + fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); + return -errno; + } + if (ntohs(server->address.sin_port) != port) { + fprintf(stderr, "[%s:%d] error: could not assign port %u\n", __func__, __LINE__, port); + return -1; + } + /* save server port number */ + server->port = port; + fprintf(stderr,"[%s]: assigned port %u\n", __func__, server->port); //ntohs(server->address.sin_port) + + /* listen for a client connection, allow 2 connections in queue */ + if (listen(server->socket, 2) == -1) { + fprintf(stderr, "[%s:%d] error %d: %s\n", __func__, __LINE__, errno, strerror(errno)); + return -errno; + } + + fprintf(stderr,"[%s]: server is up, listening for client connection\n", __func__); + return 0; +} + +/* Closes the server. */ +int server_close(struct server_t *server) { + /* force closing in case of error */ + if (close(server->socket) == -1) { + close(server->socket); + } + fprintf(stderr,"[%s]: socket closed, server is down\n", __func__); + return 0; +} + +/* Accepts incoming client connection. */ +int server_accept(struct server_t *server, struct client_t *accepted_client) { + + int namelen; + + /* accept connection request */ + namelen = sizeof(accepted_client->address); + accepted_client->socket = accept(server->socket, (struct sockaddr *) &accepted_client->address, &namelen); + if (accepted_client->socket == -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, + accepted_client->ip_string, INET_ADDRSTRLEN); + + /* print client information */ + //TODO also print timestamp + fprintf(stderr, "[%s]: accepted client %s on port %u\n", __func__, + accepted_client->ip_string, server->port); + return 0; +} + +/* Rejects incoming client connection. */ +int server_reject(struct server_t *server) { + + int namelen; + struct client_t rclient; + char reject_msg[128]; + + /* accept connection request */ + namelen = sizeof(rclient.address); + rclient.socket = accept(server->socket, (struct sockaddr *) &rclient.address, &namelen); + /* send reject message */ + sprintf(reject_msg, "[%s]: port %u is already being used\n", __func__, server->port); + send(rclient.socket, reject_msg, strlen(reject_msg), 0); + /* close connection */ + close(rclient.socket); + + fprintf(stderr, "[%s]: rejected new client request, there is alredy a client connected\n", __func__, server->port); + return 0; +} diff --git a/tty.c b/tty.c new file mode 100644 index 0000000..b675f98 --- /dev/null +++ b/tty.c @@ -0,0 +1,26 @@ +#include "moxerver_include.h" + + +/* Opens the tty device and configures it. */ +int tty_open(struct tty_t *tty_dev) { + return 0; +} +/* Closes the tty device. */ +int tty_close(struct tty_t *tty_dev) { + return 0; +} + +/* Reconfigures the tty device. */ +int tty_reconfigure(struct tty_t *tty_dev, struct termios newttyset) { + return 0; +} + +/* Reads incoming data from tty device to tty data buffer. */ +int tty_read(struct tty_t *tty_dev) { + return 0; +} + +/* Sends data from a buffer to tty device. */ +int tty_write(struct tty_t *tty_dev, char *databuf, int datalen) { + return 0; +} \ No newline at end of file