https://github.com/robzwolf/pearchat.git
__ / \ / / /_ ____ / /_ / // / \/ \/ _/ / / \/ \/ __/ / / / // / / / /___/ / / / // / / // \/\,// \// //\,_/\__/
Created by Robbie Jakob-Whitworth during the summer of 2017
View this README in a markdown viewer (e.g. http://dillinger.io/) to ensure commands are rendered correctly onscreen
The idea is as follows:
There are two people, Alice and Bob, who are on the same network and wish to communicate with each other. In this scenario, Alice will be the server and Bob will be a client. However, note that Alice will also be a client. To avoid this confusion, we term Alice's computer the HOST and Bob's computer the VISITOR.
Alice (the host) needs to run a node.js instance on her computer. We will call this node.js instance the SERVER to indicate that is it the application which manages the communication between the host and the visitor. Alice will then connect to her own computer using a web browser to https://localhost:(SERVER_PORT), and Bob (the visitor) will connect to Alice via his web browser at https://(ALICE_IP):(SERVER_PORT). As both web browsers act as clients to the server, we shall call them the WEB CLIENT. This term is used to indicate that it is unimportant whether it is the host's web browser or the visitor's web browser requesting data from the server.
Through this setup, Alice and Bob can send messages to each other.
pearchat/ directory using a terminal.
3) We need to set execute permissions on the scripts in the pearchat/ssl/ directory.
Run find ssl/. -name "*.sh" | xargs chmod 777.
4) Determine the private IP address of the host and the visitor.
5) Run ssl/create_ssl.sh <HOST IP> to create a new certificate authority and a device certificate.
For example, if your host's private IP is 192.168.1.150, then you would run ssl/create_ssl.sh 192.168.1.150 from the terminal.
At the warning about deleting pre-existing certificates, type Y to confirm deletion of any pre-existing CA certificates from the ssl/ directory.
6) On both the host and the visitor, you need to install the newly created PearChat CA certificate. Securely transfer ssl/rootCA.pem to the visitor machine, then follow the instructions according to your browser:
On Chrome:
1) Navigate to Settings > Manage certificates > Trusted Root Certification Authorities > Import...
2) Import the pearchat/ssl/rootCA.pem (NOT pear.crt) file. You may have to switch the file type
to All Files (*.*).
3) At the Security Warning alert box, click on Yes to install certificate.
4) Close the certificate manager and exit Chrome settings.
5) Restart Chrome.
On Firefox:
1) Navigate to Preferences > Advanced > Certificates > View Certificates > Import...
2) Import the pearchat/ssl/rootCA.pem (NOT pear.crt) file.
3) Tick Trust this CA to identify web sites at the Downloading Certificate window.
4) Click OK to import the certificate.
5) Click OK to close the certificate manager. Exit preferences.
There is no need to restart Firefox.
7) Run node server_https.js to start the PearChat server on the default port (8443).
To specify another port, simply run node server_https.js [port]. For example, to run the server on port 8000, run node server_https.js 8000.
8) Navigate to https://(HOST IP):(SERVER_PORT) on the visitor and https://localhost:(SERVER_PORT) on the host.
rootCA.pem certificate from both the host's and visitor's browsers, and then repeat steps 2-5 above.
server_https.js will run on port 8443. This can be changed by specifying the port from the command line. For example, to run the server on port 8000, run node server_https.js 8000.
https://(HOST_IP):(SERVER_PORT).
3) Host connects to https://localhost:(SERVER_PORT).
4) A new ChatSession object is created on the server.
If the host access https://localhost:(SERVER_PORT) before the visitor does, they will be greeted with a message (views/ejs_pages/no_unpaired_sessions.js) asking them to refresh the page after the visitor has connected to https://(HOST_IP):(SERVER_PORT).
node.js runs server_https.js on the server. This listens for HTTPS connections on port 8443 by default. See the note on custom ports above to run on a different port.
Various URLs are treated as follows:
wcToken) will set visitorIP to the ChatSession associated with that wcToken's visitor IP.
Returns a JSON object with the host IP (hostIP) and the visitor IP (visitorIP).
Used for printing IPs on the web client side.
JSON response object is as follows:
{
"host":
generateRandomToken(). Used as an identifier for messages on both the host and visitor.
JSON response object is as follows:
{
"mToken":
ChatSession is created. If the web client is a host, we check for an existing ChatSession and attach them to it, otherwise we send the no_unpaired_sessions.ejs page.
Sends the user interface for PearChat (views/ejs_pages/chat_client_interface.ejs) to whoever is requesting it (either the host or the visitor).
A random identifier token for the web client is generated and sent with the page (embedded in an inline script). If the web client is a visitor, a new ChatSession is created and the token is attached to it. If the web client is a host, and a ChatSession already exists, we locate that ChatSession and attach our newly created host's token to it.
"message"), its identifying message token ("mToken"), the web client's identifying token ("wcToken") and the timestamp at which it was sent ("timestamp").
When the message has been accepted, we call asyncChatSendResponse(), which sends a JSON response back to the web client acknowledging receipt of the message. If the web client does not receive a success:true response, then the web client handles this appropriately.
JSON response object is as follows:
{
"success": true,
"mToken":
wcToken in the JSON object they POST.
The web client sends an AJAX request to /chat_pull, and the server checks if there are any messages to send. If not, the server holds the connection open (by not responding at all) until either a message appears in the queue, or the web client's connection times out (as specified by PearChat.TIMEOUT_CONST in script.js.
The upshot of using a system like this is that we do not need to wait for the web client to poll the server for new messages. Instead, by holding the connection open and only sending a response when we have something to send, we've effectively created a framework to push data to the web client on demand, rather than them having to periodically fetch it. We call this framework 'long polling'.
chat_script.js is the main script that runs the web client. All data on the client side is stored in the window.PearChat object.
When the web client page is loaded, it goes a little like this:
1) window.PearChat is created. PearChat.wcToken is set to whatever is provided inline with the webpage (as generated by the server).
2) When the DOM is ready to be manipulated ($(document).ready()), we first make a POST request to /ip (sending our wcToken as POST data to identify us), and the server sends back our IP and the host's IP, for rendering in aside div#my-ip span and aside div#friend-ip span, respectively.
3) We then start the so-called 'long polling' (PearChat.doLongPoll()). This function does the following:
1) Makes a POST request to /chat_pull, setting the timeout to whatever was declared at the beginning of the script (PearChat.TIMEOUT_CONST) and sending our wcToken and our timeout constant in the POST data.
2) If we receive a response, parse the response for messages and render them on the screen as appropriate.
3) If we do not receive a response, we wait 200ms and then call the function again.
When the user clicks on 'Submit', the following happens:
1) Cancel the default submit action (e.preventDefault()).
2) Check if the user input is empty or entirely white space. If so, exit the function.
3) Get the time now (prettyTimeNow(), from time.js).
4) Request an mToken - a unique identifying token for this message - by sending a GET request to /gen_mtoken.
5) Draw the message on screen with the faded-msg CSS class to signify that it is still sending.
5) POST, to /chat_send, the message text, the requested mToken, our wcToken and the timestamp of the message.
6) Assuming we receive success:true in the response from the server, remove the faded-msg class from the message DOM object to signify that it has been successfully sent to the server.
Note: In practice, this can often happen so quickly that it is not even visible that the DOM message has been rendered as faded. Try sending lots of messages in quick succession to see this more visibly.
prettyTimeNow(), which returns the current timestamp in the format hh:mm:ss.
URLs above.
When node server_https.js is run, the following happens:
1) Attempt to read the server certificate and the server's private key from the ssl/ directory. If we fail, print an error to the console and exit.
2) Check if a port was specified on the command line. If not, use the default 8443, otherwise use the one specified by the user.
3) Setup express and URL-handling.
4) Start the HTTPS server.
response back using res and print it to the console.
::ffff:10.0.2.71. This function just returns the last part - in this example, 10.0.2.71.
crypto library and stores it in the global usedTokens array to ensure that token is never used again.
Technically, if we use up all available 20-byte tokens, this function will loop forever.
time.js.
Returns a random integer between min and max, inclusive.
"host" or "visitor", depending on whether the provided request IP (req_ip) is the host's IP address or the visitor's IP address, respectively.
ChatSession object which has the provided wcToken associated with either that ChatSession's host or visitor.
visitor - Visitor's IP address startTime - Start time of the chat session messages - Messages that have already been sent, these can probably be periodically removed pendingHMessages - Messages sent FROM the visitor that are waiting to be sent TO the host pendingVMessages - Messages sent FROM the host that are waiting to be sent TO the visitor sToken - ChatSession identifier token hToken - Host identifier token vToken - Visitor identifier token hostHasJoined - Has the host yet been sent their hToken? visitorHasJoined - Has the visitor yet been sent their vToken?
sender - IPv4 address of the sender (corresponding to either the host or visitor) content - Message text mToken - Message identifier token timestamp - Timestamp of the message
ssl/ directory) and an associated certificate for sending with HTTPS requests.
1) Checks the user supplied a local IP address when running the script. Warns the user and exits if they didn't.
2) Warns the user that this script will delete all pre-existing certificates in the ssl/ directory and prompts them to continue.
3) Runs ssl/create_ca.sh, which creates the certificate authority.
4) Runs ssl/create_ct.sh <host IP>, which create the certificate for the host IP address.
vToken - visitor identifier token
hToken - host identifier token
sToken - session identifier token (ChatSession unique identifier)
mToken - message identifier token
wcToken - web client identifier token (used by the web client as it does not know whether it is a host or a visitor)