diff --git a/client.c b/client.c index 75c26f9..66495ce 100644 --- a/client.c +++ b/client.c @@ -83,45 +83,66 @@ int client_write(struct client_t *client, char *databuf, int datalen) { return len; } +/* Waits for client input in "line mode". Blocks until input arrives. */ +int client_wait_line(struct client_t *client) { + + int ret; + fd_set read_fds; + + client->data[0] = '\0'; + /* loop waiting for client input */ + while (client->data[0] == '\0') { + /* setup select() parameters */ + FD_ZERO(&read_fds); + FD_SET(client->socket, &read_fds); + /* send prompt character to client */ + client_write(client, "> ", 2); + /* block until input arrives */ + if (select((client->socket)+1, &read_fds, NULL, NULL, NULL) == -1) return -1; + if (FD_ISSET(client->socket, &read_fds)) { + /* read client input */ + ret = client_read(client); + if (ret == -1) return -1; + /* we don't want empty data so stop on \r or \n */ + if ( (client->data[0] == '\r') || (client->data[0] == '\n') ) { + fprintf(stderr, "client data is empty\n"); + client->data[0] = '\0'; + } + } + } + + fprintf(stderr, "client data: %s\n", client->data); + return 0; +} + /* Waits for client to provide a username. Blocks until a username is entered. */ int client_ask_username(struct client_t *client) { int i; - char databuf[DATABUF_LEN]; - fd_set read_fds; + char msg[DATABUF_LEN]; /* send username request to client */ - snprintf(databuf, DATABUF_LEN, + snprintf(msg, DATABUF_LEN, "\nPlease provide a username to identify yourself to other users (max %d characters):\n", USERNAME_LEN); - client_write(client, databuf, strlen(databuf)); + client_write(client, msg, strlen(msg)); /* wait for client input */ - client->username[0] = '\0'; - while (client->username[0] == '\0') { - /* send prompt character to client */ - snprintf(databuf, DATABUF_LEN, "> "); - client_write(client, databuf, strlen(databuf)); - /* setup select() parameters */ - FD_ZERO(&read_fds); - FD_SET(client->socket, &read_fds); - select((client->socket)+1, &read_fds, NULL, NULL, NULL); - if (FD_ISSET(client->socket, &read_fds)) { - client_read(client); - /* handle client input */ - for (i = 0; i < USERNAME_LEN; i++) { - if (client->data[i] == '\r') { - break; - } - client->username[i] = client->data[i]; - } - client->username[i+1] = '\0'; + if (client_wait_line(client) != 0) return -1; + + /* save received data as client username */ + for (i = 0; i < USERNAME_LEN; i++) { + if ( (client->data[i] == '\r') || (client->data[i] == '\n') ) { + /* don't include \r or \n in username */ + client->username[i] = '\0'; + break; } + client->username[i] = client->data[i]; } /* send welcome message to client */ - snprintf(databuf, DATABUF_LEN, + snprintf(msg, DATABUF_LEN, "\nWelcome %s!\n\n", client->username); - client_write(client, databuf, strlen(databuf)); + client_write(client, msg, strlen(msg)); return 0; } diff --git a/moxerver.c b/moxerver.c index b4ecc0a..fd66249 100644 --- a/moxerver.c +++ b/moxerver.c @@ -215,9 +215,22 @@ int main(int argc, char *argv[]) { /* put client in "character" mode */ telnet_set_character_mode(&client); } - /* reject connection request if a client is already connected */ + /* if a client is already connected then new clients need to be handled */ else { - server_reject(&server, &client); + /* decide if current client needs to be dropped or new client needs to be rejected */ + ret = server_drop(&server, &client); + if (ret < 0) { + /* error in handling new clients, print but continue waiting for new clients */ + fprintf(stderr, "[%s] problem handling new client request\n", NAME); + continue; + } + /* if current client was dropped we need to set up new client */ + if (ret > 0) { + /* ask client to provide a username before going to "character" mode */ + client_ask_username(&client); + /* put client in "character" mode */ + telnet_set_character_mode(&client); + } } } /* check client status if connected */ diff --git a/moxerver.h b/moxerver.h index 9b8d2d4..797d123 100644 --- a/moxerver.h +++ b/moxerver.h @@ -91,12 +91,15 @@ int server_close(struct server_t *server); int server_accept(struct server_t *server, struct client_t *accepted_client); /** - * Rejects incoming client connection. + * Asks new incoming client connection if current client needs to be dropped. + * If current client doesn't need to be dropped then new client is rejected. * * Returns: - * 0 always, errors with rejected client are ignored + * - positive value if current client was dropped + * - 0 if new client was rejected + * - negative value if error occurred */ -int server_reject(struct server_t *server, struct client_t *client); +int server_drop(struct server_t *server, struct client_t *client); /* Functions handling communication with clients. */ @@ -129,12 +132,23 @@ int client_read(struct client_t *client); */ int client_write(struct client_t *client, char *databuf, int datalen); +/** + * Waits for client input in "line mode", where client sends a whole line of characters. + * Blocks until input arrives. + * + * Returns: + * - 0 on success + * - negative value if error occurred + */ +int client_wait_line(struct client_t *client); + /** * Waits for client to provide a username. * Blocks until a username is entered. * * Returns: - * 0 always + * - 0 on success + * - negative value if error occurred */ int client_ask_username(struct client_t *client); diff --git a/server.c b/server.c index 17f6760..a068d61 100644 --- a/server.c +++ b/server.c @@ -103,26 +103,53 @@ int server_accept(struct server_t *server, struct client_t *accepted_client) { return 0; } -/* Rejects incoming client connection. Errors with rejected client are ignored. */ -int server_reject(struct server_t *server, struct client_t *client) { +/* Drops current client or rejects new client. */ +int server_drop(struct server_t *server, struct client_t *current_client) { - int namelen; - struct client_t rclient; - char reject_msg[128]; + struct client_t new_client; + char msg[DATABUF_LEN]; char timestamp[TIMESTAMP_LEN]; - /* accept connection request */ - namelen = sizeof(rclient.address); - rclient.socket = accept(server->socket, (struct sockaddr *) &rclient.address, (socklen_t *) &namelen); - /* send reject message to client */ - time2string(client->last_active, timestamp); - sprintf(reject_msg, "\nPort %u is already being used:\ncurrent user: %s\nlast activity: @ %s\n\n", - server->port, client->username, timestamp); - send(rclient.socket, reject_msg, strlen(reject_msg), 0); - /* close connection */ - close(rclient.socket); + /* accept new connection request */ + if (server_accept(server, &new_client) != 0) return -1; + + /* inform new client that port is already in use */ + time2string(current_client->last_active, timestamp); + sprintf(msg, "\nPort %u is already being used!\nCurrent user and last activity:\n%s @ %s\n", + server->port, current_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):"); + send(new_client.socket, msg, strlen(msg), 0); + + /* wait for client input */ + if (client_wait_line(&new_client) != 0) return -1; + + /* check client confirmation */ + if (strncmp(new_client.data, "YES DROP", 8) == 0) { + /* current client should be dropped, drop it */ + client_close(current_client); + time2string(time(NULL), timestamp); + fprintf(stderr, "[%s] dropped current client %s on port %u @ %s\n", __func__, + current_client->ip_string, server->port, timestamp); + /* make new client the current client */ + memcpy(current_client, &new_client, sizeof(struct client_t)); + time2string(time(NULL), timestamp); + fprintf(stderr, "[%s] accepted new client request %s on port %u @ %s\n", __func__, + current_client->ip_string, server->port, timestamp); + /* return 1 because current client was dropped */ + return 1; + } + else { + /* new connection should be rejected, close connection */ + close(new_client.socket); + time2string(time(NULL), timestamp); + fprintf(stderr, "[%s] rejected new client request %s on port %u @ %s\n", __func__, + new_client.ip_string, server->port, timestamp); + /* return 0 because new client was rejected */ + return 0; + } + - time2string(time(NULL), timestamp); - fprintf(stderr, "[%s] rejected new client request @ %s\n", __func__, timestamp); - return 0; }