Mini-Discord is a multi-threaded console Java application that handles multiple client requests and conversations at the same time. it consists of main two parts Client Application
and Server Application
. In the following section, I'll describe how they work internally and show the features with screenshots.
The server is the main part of the Mini Discord application. it starts to listen on the specified port and connect with clients to handle communication and requests.
the most important role of the server is handling multiple requests simultaneously, each client has its own Client Handler
which is responsible for sending and receiving messages, and commands from and to the client.
What happens under the hood?
- A new client opens the client from its end, this will inform the server that a new lobby client is active now.
- The server will accept the new client request, then create a new
client handler
and start a new thread for this handler and start it.
// ---- code snippet ----
Socket clientSocket = serverSocket.accept();
// generate a temporary name for lobby clients.
String tempUsername = "lobby-client-" + DateTimeHandler.timestampNow();
logger.logThis("info", "Listening for client requests. . .");
logger.logThis("request", "A new lobby client request is accepted");
ClientHandler clientHandler = new ClientHandler(clientSocket, tempUsername);
Thread thread = new Thread(clientHandler);
thread.start();
// ---- code snippet ----
- By Client handler constructor take the
socket
, andusername
as arguments to initialize the client handler object by setting the username for this, and the most important role here is to addthis
-newly created- to theclientsList
which hold all current active handlers.
class ClientHandler implements Runnable {
/* ---- code snippet ---- */
// shared by all handlers
private static ArrayList<ClientHandler> clientsList = new ArrayList<>();
public ClientHandler(Socket socket, String username) {
try {
this.socket = socket;
this.bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
this.bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.username = username;
clientsList.add(this);
} catch (IOException e) {
e.printStackTrace();
}
}
/* ---- code snippet ---- */
}
-
By starting the client thread, it will trigger the Overrided
run
method from theRunnable
interface. which will handle the incoming client requests by the specified flag in the request message. -
Each client request to be handled by the handler must consist of FLAG, DELIMITER, and PAYLAOD. handler will parse it choose which function will be executed and replay it to the client.
Parsing incoming commands from the client:
// reading commands from the client side.
command = bufferedReader.readLine();
String[] commandArray = command.split(Settings.DELIMITER);
String flag = commandArray[0];
String payload = commandArray[1];
Choose which function will be executed:
if(flag.equals("[RESET]"))
{
logger.logThis("request", "Reset password request is captured");
Credentials credentials = credentialsService.deSerialize(payload);
String username = credentials.getUsername();
String password = credentials.getPassword();
// hashing the new password
password = HashingService.getSha256(password);
String result = databaseAPI.updatePassword(username, password);
logger.logThis("info", "Account updated [" + result + "]");
sendMessage(this, result);
}
The clients send the user message that applies the following structure
FLAG DELIMITER PAYLOAD
Note: No spaces between them, for example of [VALIDATE]
usage.
[VALIDATE]>hossam
| | |
| | (Username)
| (DELIMITER)
(FLAG)
Here is a list of all available and handled flags on the server side with their descriptions.
Flag | Server usage | Followed By |
---|---|---|
[SIGNUP] |
Signup a new user | Serialized User object |
[LOAD] |
Read the user from the database then return it to the client | Username |
[RESET] |
Reset user password | Serialized Credentials object |
[VALIDATE] |
Validate if the username is exist in database | Username |
[UPDATE] |
Update the user words and conversations | Serialized User object |
[SNAPSHOT] |
Ask the server to return conversation | Username |
[AUTH] |
Authorize user in login process | Serialized Credentials object |
[JOIN] |
Add user to the chat room | Username |
[BROADCAST] |
Send message to all peers in chat room | "{sendername}~{message}" |
[LEAVE] |
inform all users a member has left | Username |
Files in | Description |
---|---|
database | The database API responsible for dealing with the database and tasks such as adding new user , update user statistics , etc. |
model | the main object classes that the server and the client exchange together |
server | contains the server backend logic such as running the server , and handling incoming requests |
service | all needed functionality by the models such as serialization , deserialization , and hashing function |
utils | all utility functions such as logging, colored output, banners, etc. |
Settings.java | all settings needed by the server is here such as port , host , database connection , etc. |
- Secured stored credentials
All confidential data is stored hashed in the database using the
SHA256
algorithm. fromguava
library
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>
public static String getSha256(String plainPassword) throws NoSuchAlgorithmException {
return Hashing.sha256()
.hashString(plainPassword, StandardCharsets.UTF_8)
.toString();
}
- Logging events
I have implemented logging events handler
/utils/Logger.java
to enable logging events and verbosity to make it easier to know what is happening right now with each event.
- Settings class
in the
Settings.java
you can specify the settings of the server such as the following.
public class Settings {
public static final Integer PORT = 5000;
public static final String DATABASE_USERNAME = "_USERNAME_";
public static final String DATABASE_PASSWORD = "_PASSWORD_";
private static final String DATABASE_NAME = "DATABASE_NAME";
public static final String DATABASE_URI = "jdbc:mysql://localhost:3306/" + DATABASE_NAME;
// this property must be set in the client too
// it's the DELIMITER between flag and payload.
public static final String DELIMITER = ">";
}
The client is the second important part of the application, by running the client
it will try to connect to the server on the specified host and port number in the Settings.java
. seeing the prompt
means you can ask the server for the actions you need in your current mode.
The client has 3 main modes:
Mode | Description | How to active? |
---|---|---|
Lobby | The default mode when opening the client | default |
Active | The mode loaded after login, your profile is loaded here to be ready for dumping | /login |
Chatting | Mode in the chat room, in which you send messages to all peers | /join |
Use /help
to get the current available commands for Lobby
, and Active
modes.
Files in | Description |
---|---|
model | The main object classes that the server and the client exchange together |
network | Connection handlers such as client connection handler , and message listener |
service | All needed functionality by the models such as serialization , deserialization , hashing , reporting functions |
ui | All ui modes and functions such as colored output, banners, and login , signup , and reset password , etc. |
utils | All utility functions such as date and time, and used validations. |
Settings.java | All settings needed by the client is here such as port , host , and delimiter , etc. |
-
Signup Passwords are taken from the user via
console.readPassword()
which allows you to take passwords hidden, with no echo in the console. Username validation also happens during the filling of account data. -
Signin
- Reset Password
- Join Chat
Users try to join the room using
/join
command in the active mode. if the user faces any problem he will try to use the command again until joining the room. All messages will be broadcast to the peers in the room.
if the user uses /leave
or bye bye
command he will leave the room and then ask the server for a snapshot of the conversation till the moment he leaves.
There are 4 file dumps that happened here
/{username}/conversation-temp.txt
contains this conversation message./{username}/conversation-total.txt
contains all-time conversation words/{username}/words-temp.txt
contains these conversation words and their frequency./{username}/words-total.txt
contains all-time words and their frequency.
- Visualize your statistics
- help commands
- First clone this repository
git clone https://github.com/0xGhazy/mini-discord.git
2- Create a new database with the following command
CREATE DATABASE `mini_discord`
3- Create User table inside the database
CREATE TABLE `users` (
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`secQ` varchar(255) NOT NULL,
`secA` varchar(255) NOT NULL,
`words` blob,
`conversations` blob,
PRIMARY KEY (`username`)
)
4- Open the server project directory with your favorite IDE and run the server first. Make sure that the server is running like the following image:
In my case, IntelliJ IDE has downloaded all dependencies, so make sure your pom.xml file dependencies are downloaded successfully in the client and server code.
5- Open the client project directory with your favorite IDE and run the client. Using Visual Code is preferred because of its integrated terminal that allows us to use the console module for better password taking
Happy chatting and dumping 🎉😊
0xGhazy/fateh-framework Is a free, open-source tool targeting Windows-systems Based on HTTP reversed shell. This tool helps you to generate Fully UnDetectable (FUD) HTTP reversed shell With many features that you will find mentioned below. that was programmed primarily for educational and self-challenging purpose.