diff --git a/chatserver/README.md b/chatserver/README.md index ac65127..47301bf 100644 --- a/chatserver/README.md +++ b/chatserver/README.md @@ -5,8 +5,4 @@ Use `python server.py` to run the server Use the client to communicate with the chatserver by running `python client.py` ## chatserver information -Included is the server and client written in Python. - -## pcap information -A demonstration of the chatserver has been captured with Wireshark in the -included `demo.pcap` file. +Included is the server and client written in Python. \ No newline at end of file diff --git a/chatserver/client.py b/chatserver/client.py index e69de29..74ccf2b 100644 --- a/chatserver/client.py +++ b/chatserver/client.py @@ -0,0 +1,77 @@ +import socket +import sys +import select +import os + +BUFFER_SIZE = 1024 +DEFAULT_HOST = 'localhost' +DEFAULT_PORT = 8181 + +def main(): + if len(sys.argv) != 3: + SERVER_HOST = input(f"Enter server host (default: {DEFAULT_HOST}): ") or DEFAULT_HOST + SERVER_PORT = input(f"Enter server port (default: {DEFAULT_PORT}): ") or DEFAULT_PORT + SERVER_PORT = int(SERVER_PORT) + else: + SERVER_HOST = sys.argv[1] + SERVER_PORT = int(sys.argv[2]) + + # create a socket object + client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + # connect to the server + client_socket.connect((SERVER_HOST, SERVER_PORT)) + print(f"Connected to chat server at {SERVER_HOST}:{SERVER_PORT}") + + if os.name == 'nt': # Windows-specific logic + import msvcrt + client_socket.setblocking(False) # Set the socket to non-blocking mode + + while True: + # Check for server messages + try: + server_message = client_socket.recv(BUFFER_SIZE).decode('utf-8') + if server_message: + print(f"\n{server_message}") + print("> ", end="", flush=True) + except BlockingIOError: + pass + + # Check for user input + if msvcrt.kbhit(): + client_message = input("> ") + if client_message.strip() == "%!": + print("Exiting chat...") + break + client_socket.send(client_message.encode('utf-8')) + + else: # Linux/Unix-specific logic + while True: + # use select to wait for either server messages or user input + read_sockets, _, _ = select.select([client_socket, sys.stdin], [], []) + + for sock in read_sockets: + if sock == client_socket: + # receive message from server + server_message = client_socket.recv(BUFFER_SIZE).decode('utf-8') + if not server_message: + print("Disconnected from server") + return + print(f"\n{server_message}") + print("> ", end="", flush=True) + else: + # prompt for user input + client_message = input() + if client_message.strip() == "%!": + print("Exiting chat...") + return + client_socket.send(client_message.encode('utf-8')) + + except Exception as e: + print(f"Error: {e}") + finally: + client_socket.close() + print("Connection closed") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/chatserver/server.py b/chatserver/server.py index fd79b83..40978b3 100644 --- a/chatserver/server.py +++ b/chatserver/server.py @@ -1,71 +1,414 @@ import socket import select +import uuid +import pickle +import os +import threading +from flask import Flask, request, jsonify +from flask_socketio import SocketIO, emit, join_room, leave_room +from flask_cors import CORS +from datetime import datetime # Import for timestamps +import markdown # Import the markdown library + +# Flask app for REST API and WebSocket +app = Flask(__name__) +socketio = SocketIO(app, cors_allowed_origins="*") # Allow all origins + +# Enable CORS for the Flask app +CORS(app) CHATPORT = 8181 BUFFER_SIZE = 1024 MAX_CONNECT_QUEUE = 10 +DATA_FILE = 'server_data.pkl' +UUID_FILE = 'uuids.pkl' -def main(): - # set the flag variable to true for infinite loop - quitFlag = True - # create a socket object +# Global variables for chat server +socket_list = [] +client_info = {} +chat_rooms = {} +client_states = {} +uuid_dict = {} +message_history = {} # New: Store message history for each chat room +server_socket = None +quitFlag = True + +# Utility functions +def save_data(client_info, chat_rooms, client_states): + serializable_client_info = {info["user_id"]: info for sock, info in client_info.items()} + serializable_chat_rooms = {info["user_id"]: chat_rooms[sock] for sock, info in client_info.items()} + serializable_client_states = {info["user_id"]: client_states[sock] for sock, info in client_info.items()} + with open(DATA_FILE, 'wb') as f: + pickle.dump((serializable_client_info, serializable_chat_rooms, serializable_client_states), f) + +def load_data(): + if os.path.exists(DATA_FILE): + with open(DATA_FILE, 'rb') as f: + serializable_client_info, serializable_chat_rooms, serializable_client_states = pickle.load(f) + client_info = {uuid.UUID(user_id): info for user_id, info in serializable_client_info.items()} + chat_rooms = {uuid.UUID(user_id): room for user_id, room in serializable_chat_rooms.items()} + client_states = {uuid.UUID(user_id): state for user_id, state in serializable_client_states.items()} + return client_info, chat_rooms, client_states + return {}, {}, {} + +def save_uuids(uuid_dict): + with open(UUID_FILE, 'wb') as f: + pickle.dump(uuid_dict, f) + +def load_uuids(): + if os.path.exists(UUID_FILE): + with open(UUID_FILE, 'rb') as f: + return pickle.load(f) + return {} + +def get_client_uuid(client_socket, uuid_dict): + client_address = client_socket.getpeername() + client_key = f"{client_address[0]}_{client_address[1]}" + + if client_key in uuid_dict: + user_id = uuid_dict[client_key] + else: + user_id = str(uuid.uuid4()) + uuid_dict[client_key] = user_id + save_uuids(uuid_dict) + + return user_id + +def handle_message(username, chat_room, message): + """Store the message in history and broadcast it to all clients.""" + # Add a timestamp to the message + timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + # Convert the message to HTML using Markdown + html_message = markdown.markdown(message) + + # Store the message in the chat room's history + if chat_room not in message_history: + message_history[chat_room] = [] + message_history[chat_room].append(f"[{timestamp}] {username}: {message}") + + # Broadcast the message to WebSocket clients in the chat room + socketio.emit('message', { + 'username': username, + 'message': html_message, # Send the HTML-rendered message + 'timestamp': timestamp, + 'chat_room': chat_room + }, room=chat_room) + + # Broadcast the message to socket-based clients in the chat room + send_message = f"[{timestamp}] {username} >> {message}".encode('utf-8') + for client_sock in socket_list: + if client_sock != server_socket and chat_rooms.get(client_sock) == chat_room: + client_sock.send(send_message) + +def handle_command(username, chat_room, command, sock=None): + """Process commands sent by clients.""" + if command.strip() == "%?": + help_message = ( + "Help: (%?)\n" + "Send messages like normal. Use enter to send a message.\n" + "Available Commands:\n" + "\t%? : Show this help prompt.\n" + "\t%! : Exit from the Chat Server.\n" + "\t%* : Switch to another chatroom.\n" + "\t%$ : Change Username.\n" + "\t%@ : Send a direct message to a user.\n" + ) + if sock: + sock.send(help_message.encode('utf-8')) + else: + socketio.emit('message', {'username': 'Server', 'message': help_message}, room=chat_room) + return + + if command.strip() == "%*": + if sock: + sock.send(b"\nEnter chatroom name: \n") + client_states[sock] = "WAITING_FOR_NEW_CHATROOM" + else: + socketio.emit('message', {'username': 'Server', 'message': "Enter chatroom name:"}, room=chat_room) + return + + if command.strip() == "%$": + if sock: + sock.send(b"\nEnter new username: \n") + client_states[sock] = "WAITING_FOR_NEW_USERNAME" + else: + socketio.emit('message', {'username': 'Server', 'message': "Enter new username:"}, room=chat_room) + return + + if command.strip().startswith("%@ "): + parts = command.strip().split(' ', 2) + if len(parts) < 3: + error_message = "Invalid direct message format. Use: %@ " + if sock: + sock.send(f"\n{error_message}\n".encode('utf-8')) + else: + socketio.emit('message', {'username': 'Server', 'message': error_message}, room=chat_room) + else: + target_username = parts[1] + dm_message = parts[2] + timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + # Check if the target is a socket-based client + target_sock = None + for s, info in client_info.items(): + if not info.get("is_websocket") and info.get("username") == target_username: + target_sock = s + break + + # Check if the target is a WebSocket client + target_websocket_sid = None + for sid, info in client_info.items(): + if info.get("is_websocket") and info.get("username") == target_username: + target_websocket_sid = sid + break + + if target_sock: + # Send the direct message to the socket-based client + sender_username = username + target_sock.send(f"[{timestamp}] DM from {sender_username}: {dm_message}\n".encode('utf-8')) + if sock: + sock.send(f"[{timestamp}] Direct message sent.\n".encode('utf-8')) + elif target_websocket_sid: + # Send the direct message to the WebSocket client + socketio.emit('message', { + 'username': username, + 'message': dm_message, + 'timestamp': timestamp, + 'chat_room': 'Direct Message' + }, to=target_websocket_sid) + if sock: + sock.send(f"[{timestamp}] Direct message sent.\n".encode('utf-8')) + else: + # Target user not found + error_message = "User not found." + if sock: + sock.send(f"\n{error_message}\n".encode('utf-8')) + else: + socketio.emit('message', {'username': 'Server', 'message': error_message}, room=chat_room) + return + + if command.strip() == "%stop": + if client_info.get(sock, {}).get("is_admin"): + print("\nAdmin has stopped the server.") + global quitFlag + quitFlag = False + if sock: + sock.send(b"\nServer is stopping...\n") + else: + socketio.emit('message', {'username': 'Server', 'message': "Server is stopping..."}, room=chat_room) + else: + error_message = "You do not have permission to stop the server." + if sock: + sock.send(f"\n{error_message}\n".encode('utf-8')) + else: + socketio.emit('message', {'username': 'Server', 'message': error_message}, room=chat_room) + return + +# REST API Endpoints +@app.route('/send_message', methods=['POST']) +def send_message(): + """Send a message to a specific chat room.""" + data = request.json + username = data.get('username') + chat_room = data.get('chat_room', 'default') + message = data.get('message') + + if not username or not message: + return jsonify({"error": "Username and message are required"}), 400 + + if message.strip().startswith("%"): + handle_command(username, chat_room, message) + else: + handle_message(username, chat_room, message) + + return jsonify({"status": "Message sent and processed"}), 200 + +@app.route('/create_chatroom', methods=['POST']) +def create_chatroom(): + """Create a new chat room.""" + data = request.json + chat_room = data.get('chat_room') + + if not chat_room: + return jsonify({"error": "Chat room name is required"}), 400 + + # Add logic to create a chat room (if needed) + return jsonify({"status": f"Chat room '{chat_room}' created"}), 200 + +@app.route('/get_messages', methods=['GET']) +def get_messages(): + """Retrieve messages for a specific user (not implemented here).""" + # You can implement a message queue or buffer for each user to store messages + return jsonify({"error": "Not implemented"}), 501 + +@app.route('/get_history', methods=['GET']) +def get_history(): + """Retrieve message history for a specific chat room.""" + chat_room = request.args.get('chat_room', 'default') + if chat_room not in message_history: + return jsonify({"messages": []}) # Return an empty list if no history exists + + # Return the full message history with timestamps + return jsonify({"messages": message_history[chat_room]}) + +@app.route('/send_direct_message', methods=['POST']) +def send_direct_message(): + """Send a direct message to a specific user.""" + data = request.json + sender_username = data.get('sender_username') + target_username = data.get('target_username') + message = data.get('message') + + if not sender_username or not target_username or not message: + return jsonify({"error": "Sender username, target username, and message are required"}), 400 + + timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + # Check if the target is a socket-based client + target_sock = None + for s, info in client_info.items(): + if not info.get("is_websocket") and info.get("username") == target_username: + target_sock = s + break + + # Check if the target is a WebSocket client + target_websocket_sid = None + for sid, info in client_info.items(): + if info.get("is_websocket") and info.get("username") == target_username: + target_websocket_sid = sid + break + + if target_sock: + # Send the direct message to the socket-based client + target_sock.send(f"[{timestamp}] DM from {sender_username}: {message}\n".encode('utf-8')) + return jsonify({"status": "Direct message sent to socket-based client"}), 200 + elif target_websocket_sid: + # Send the direct message to the WebSocket client + socketio.emit('message', { + 'username': sender_username, + 'message': message, + 'timestamp': timestamp, + 'chat_room': 'Direct Message' + }, to=target_websocket_sid) + return jsonify({"status": "Direct message sent to WebSocket client"}), 200 + else: + # Target user not found + return jsonify({"error": "Target user not found"}), 404 + +# WebSocket event handlers +@socketio.on('connect') +def handle_connect(): + print("WebSocket client connected") + # Assign a unique ID to the WebSocket client + user_id = str(uuid.uuid4()) + client_info[request.sid] = {"user_id": user_id, "username": None, "is_websocket": True} + print(f"WebSocket client registered with ID: {user_id}") + +@socketio.on('disconnect') +def handle_disconnect(): + print("WebSocket client disconnected") + +@socketio.on('join') +def handle_join(data): + print(f"Received join event: {data}") + username = data.get('username') + chat_room = data.get('chat_room', 'default') + + if not username: + emit('error', {'message': 'Username is required'}) + return + + # Update client info for WebSocket clients + client_info[request.sid]["username"] = username + client_info[request.sid]["chat_room"] = chat_room + + join_room(chat_room) + emit('message', {'username': 'Server', 'message': f"{username} has joined the chat room '{chat_room}'."}, room=chat_room) + +@socketio.on('leave') +def handle_leave(data): + username = data.get('username') + chat_room = data.get('chat_room', 'default') + leave_room(chat_room) + emit('message', {'message': f"{username} has left the chat room '{chat_room}'."}, room=chat_room) + +@socketio.on('send_message') +def handle_send_message(data): + print(f"Received send_message event: {data}") + username = data.get('username') + chat_room = data.get('chat_room', 'default') + message = data.get('message') + + if not username or not message: + emit('error', {'message': 'Username and message are required'}) + return + + if message.strip().startswith("%"): + handle_command(username, chat_room, message) + else: + handle_message(username, chat_room, message) + +# Chat server logic +def chat_server(): + global quitFlag, server_socket, socket_list, client_info, chat_rooms, client_states, uuid_dict + + # Create and configure the server socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # set the socket options, SO_REUSEADDR allows to reuse the same port - # SO_KEEPALIVE enables the socket to send keepalive messages server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - # bind the socket to the address and port server_socket.bind(('', CHATPORT)) - # listen for incoming connections server_socket.listen(MAX_CONNECT_QUEUE) - # set the server socket to non-blocking server_socket.setblocking(False) - # create a list to store the sockets, server and client - socket_list = [server_socket] - # since a socket is associated with a username and a chatroom, - # and each one can only be associated one way at once, use dictionaries to - # associate with each other. - usernames = {} - chat_rooms = {} - # keep track of the states of each client connected, and if they are - # technically in an 'unsafe' state like in change username or chatroom mode. - client_states = {} - # let the user know the server is listening on port CHATPORT. - print(f"Chatserver listening on port: {CHATPORT}") + socket_list.append(server_socket) + print(f"Chat server listening on port {CHATPORT}") - # infinite loop for the program while quitFlag: - # use the select function to monitor the sockets read_sockets, _, _ = select.select(socket_list, [], []) - # loop through the sockets for sock in read_sockets: - # if the socket is the server socket, accept the connection if sock == server_socket: + # Accept new client connections client_socket, client_address = server_socket.accept() client_socket.setblocking(False) socket_list.append(client_socket) - print("\nAccepted connection from new client.") - client_socket.send(b"\nWelcome to the chat server!\n") - client_socket.send(b"\nUse '%!' to exit, or '%?' for help.\n") - client_socket.send(b"\nType in a username: \n") + print(f"Accepted connection from {client_address}") + client_socket.send(b"Welcome to the chat server!\n") + client_socket.send(b"Type in a username: \n") client_states[client_socket] = "WAITING_FOR_USERNAME" - # otherwise the socket is a client socket. + user_id = get_client_uuid(client_socket, uuid_dict) + client_info[client_socket] = {"user_id": user_id} else: - # try to receive the message from the client + # Handle client messages try: message = sock.recv(BUFFER_SIZE).decode('utf-8') if message: state = client_states.get(sock, "NORMAL") if state == "WAITING_FOR_USERNAME": - usernames[sock] = message.strip() - chat_rooms[sock] = "default" - sock.send(b"\nUsername accepted. You are now in the 'default' chatroom.\n") - client_states[sock] = "NORMAL" + username = message.strip() + if username == "admin": + sock.send(b"\nEnter admin password: \n") + client_states[sock] = "WAITING_FOR_ADMIN_PASSWORD" + client_info[sock]["username"] = username + else: + client_info[sock]["username"] = username + chat_rooms[sock] = "default" + sock.send(b"\nUsername accepted. You are now in the 'default' chatroom.\n") + client_states[sock] = "NORMAL" + elif state == "WAITING_FOR_ADMIN_PASSWORD": + password = message.strip() + if password == "adminpass": + sock.send(b"\nAdmin authenticated successfully.\n") + client_info[sock]["is_admin"] = True + chat_rooms[sock] = "default" + client_states[sock] = "NORMAL" + else: + sock.send(b"\nInvalid admin password. Please enter a username: \n") + client_states[sock] = "WAITING_FOR_USERNAME" elif state == "WAITING_FOR_NEW_USERNAME": - usernames[sock] = message.strip() + client_info[sock]["username"] = message.strip() sock.send(b"\nUsername changed successfully.\n") - print(f"\nClient switched username to {usernames[sock]}.") + print(f"\nClient switched username to {client_info[sock]['username']}.") client_states[sock] = "NORMAL" elif state == "WAITING_FOR_NEW_CHATROOM": old_chatroom = chat_rooms[sock] @@ -79,37 +422,22 @@ def main(): if message.strip() == "%!": sock.close() socket_list.remove(sock) - del usernames[sock] + del client_info[sock] del chat_rooms[sock] del client_states[sock] print("\nClient has closed connection.") - elif message.strip() == "%?": - print("\nClient requested help.") - sock.send(b"Help: (%?)\n") - sock.send(b"Send messages like normal. Use enter to send a message.\n") - sock.send(b"Available Commands:\n") - sock.send(b"\t%? : Show this help prompt.\n") - sock.send(b"\t%! : Exit from the Chat Server.\n") - sock.send(b"\t%* : Switch to another chatroom.\n") - sock.send(b"\t%$ : Change Username.\n") - elif message.strip() == "%*": - sock.send(b"\nEnter chatroom name: \n") - client_states[sock] = "WAITING_FOR_NEW_CHATROOM" - elif message.strip() == "%$": - sock.send(b"\nEnter new username: \n") - client_states[sock] = "WAITING_FOR_NEW_USERNAME" else: - username = usernames[sock] - chat_room = chat_rooms[sock] - send_message = f"{username} >> {message}".encode('utf-8') - for client_sock in socket_list: - if client_sock != server_socket and chat_rooms.get(client_sock) == chat_room: - client_sock.send(send_message) + if message.strip().startswith("%"): + handle_command(client_info[sock]["username"], chat_rooms[sock], message, sock) + else: + username = client_info[sock]["username"] + chat_room = chat_rooms[sock] + handle_message(username, chat_room, message) else: sock.close() socket_list.remove(sock) - if sock in usernames: - del usernames[sock] + if sock in client_info: + del client_info[sock] if sock in chat_rooms: old_chatroom = chat_rooms[sock] del chat_rooms[sock] @@ -122,8 +450,8 @@ def main(): print(f"Error: {err}") sock.close() socket_list.remove(sock) - if sock in usernames: - del usernames[sock] + if sock in client_info: + del client_info[sock] if sock in chat_rooms: old_chatroom = chat_rooms[sock] del chat_rooms[sock] @@ -133,7 +461,14 @@ def main(): del client_states[sock] print("\nClient has closed connection.") + # Save data before shutting down + save_data(client_info, chat_rooms, client_states) server_socket.close() if __name__ == "__main__": - main() \ No newline at end of file + # Start the chat server in a separate thread + server_thread = threading.Thread(target=chat_server, daemon=True) + server_thread.start() + + # Start the Flask REST API and WebSocket server + socketio.run(app, host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/chatserver/templates/index.html b/chatserver/templates/index.html new file mode 100644 index 0000000..4018ba3 --- /dev/null +++ b/chatserver/templates/index.html @@ -0,0 +1,376 @@ + + + + + + Web Chat Client + + + + + + + +
+ Username: | Chat Room: | Server: +
+ + + + + +
+
+ + +
+ + +
+ + +
+ + + + \ No newline at end of file diff --git a/chatserver/webclient.py b/chatserver/webclient.py new file mode 100644 index 0000000..2a1856c --- /dev/null +++ b/chatserver/webclient.py @@ -0,0 +1,10 @@ +from flask import Flask, render_template + +app = Flask(__name__) + +@app.route('/') +def index(): + return render_template('index.html') # Serve the HTML client + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=8080) # Run the web client on port 8080 \ No newline at end of file diff --git a/echoserver/Makefile b/echoserver/Makefile deleted file mode 100644 index 221b29f..0000000 --- a/echoserver/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -CC = gcc -Wall -std=c11 -pedantic -SOURCES = echoserver.c -OBJECTS = echoserver.o - -all: echoserver - -echoserver: $(OBJECTS) - $(CC) $(OBJECTS) -o echoserver - -echoserver.o: echoserver.c - $(CC) -c echoserver.c -o echoserver.o - -clean: - rm -f $(OBJECTS) echoserver echoserver.out - -clean_objects: - rm -f $(OBJECTS) - -run: - ./echoserver diff --git a/echoserver/README.md b/echoserver/README.md deleted file mode 100644 index fb81333..0000000 --- a/echoserver/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Running the echoserver -Use `make` to compile the echoserver. -Use `make run` to run the echoserver. This will run on an infinite loop. -Use `Ctrl+C` to stop the echoserver. - -## echoserver communication -Communicating with the server can happen using telnet in TCP mode. - -## echoserver information -The echoserver runs on TCP port 8888. \ No newline at end of file diff --git a/echoserver/echoserver.c b/echoserver/echoserver.c deleted file mode 100644 index 73e2bbc..0000000 --- a/echoserver/echoserver.c +++ /dev/null @@ -1,150 +0,0 @@ -// header files -#include "echoserver.h" - -// constants -// echoserver port is 8888 -#define ECHOPORT 8888 -#define BUFFER_SIZE 1024 -#define MAX_CONNECT_QUEUE 10 - -// prototypes - -// main function -int main(int argc, char **argv) -{ - // initialize program (variables and structs) - bool quitFlag = true; - int socketEchoFD; - int clientEchoFD; - int reuseAddrOpt = 1; - int keepAliveOpt = 1; - char message[BUFFER_SIZE]; - int messageBytes; - struct sockaddr_in serverAddr; - struct sockaddr_storage clientAddr; - socklen_t clientSize; - memset(&serverAddr, 0, sizeof(serverAddr)); - memset(&clientAddr, 0, sizeof(clientAddr)); - // define a TCP socket - socketEchoFD = socket(AF_INET, SOCK_STREAM, 0); - if(socketEchoFD < 0) - { - printf("Unable to create the socket.\n"); - return -1; - } - // use the setsockopt function to make the socket accept different configurations - // allow for the reuse of the address/bind. - setsockopt(socketEchoFD, SOL_SOCKET, SO_REUSEADDR, &reuseAddrOpt, sizeof(reuseAddrOpt)); - // also need to set the socket to keepalive using setsockopt - setsockopt(socketEchoFD, SOL_SOCKET, SO_KEEPALIVE, &keepAliveOpt, sizeof(keepAliveOpt)); - // bind the TCP socket to port 8888 / ECHOPORT - serverAddr.sin_family = AF_INET; - serverAddr.sin_addr.s_addr = INADDR_ANY; - serverAddr.sin_port = htons(ECHOPORT); - int bindCheck = bind(socketEchoFD, &serverAddr, sizeof(serverAddr)); - if(bindCheck < 0) - { - printf("Unable to bind the socket to the port or address.\n Is either in use?"); - return -1; - } - // use fnctl to set the non-blocking flag for the socket - fcntl(socketEchoFD, F_SETFL, O_NONBLOCK); - // the server will now accept client connections by listening. - listen(socketEchoFD, MAX_CONNECT_QUEUE); - // create a file descriptor set, to keep track of the sockets in use using fd_set and FD_ZERO? - fd_set socketList; - FD_ZERO(&socketList); - // add the socketEchoFD to the file descriptor set using fd_set - FD_SET(socketEchoFD, &socketList); - // max file descriptor, this will be used to 'find' the socket currently connected - int maxFD = socketEchoFD; - // while loop that will continue to listen for messages from clients. - printf("Echoserver listening on port: %d\n", ECHOPORT); - while (quitFlag) - { - // set of active connections - fd_set activeSet = socketList; - // use the select() function here to check the set of sockets - int selectedFD = select(maxFD+1, &activeSet, NULL, NULL, NULL); - // check to make sure select() didn't error out. - if(selectedFD == -1) - { - // - printf("\nUnable to select socket to listen to.\n"); - return -1; - } - // for loop that loops up to the max file descriptor. - for(int fileIter = 0; fileIter <= maxFD; fileIter++) - { - // check to see if the file descriptor is in the set of sockets using FD_ISSET() - if(FD_ISSET(fileIter, &activeSet)) - { - // if the server socket is being written to/read from, - if(fileIter == socketEchoFD) - { - // create a client socket using accept - clientSize = sizeof(clientAddr); - clientEchoFD = accept(socketEchoFD, &clientAddr, &clientSize); - // check to see if the client socket is connected (-1) - if(clientEchoFD == -1) - { - printf("\nCould not connect the client to a socket.\n"); - return -1; - } - else - { - // set the non blocking flag using fnctl - fcntl(clientEchoFD, F_SETFL, O_NONBLOCK); - // add the client socket to the set of file descriptor sockets - FD_SET(clientEchoFD, &socketList); - // check to see if the new socket FD is greater than the current max (clientSocket > maxFD) - if(clientEchoFD > maxFD) - { - // if it is, set the new max. - maxFD = clientEchoFD; - } - printf("\nConnected client to socket.\n"); - } - printf("\nAccepted connection from new client.\n"); - } - // if the client socket(s) are being written to/read from, - else - { - // grab the data from the client, save to message. - messageBytes = recv(fileIter, message, BUFFER_SIZE, 0); - // make sure that the connection is still open before attempting to send. - if(messageBytes <= 0) // old: messageBytes <= 0 - { - // if it is not open, close the socket and remove the socket from the socket list using FD_CLR() - close(fileIter); - FD_CLR(fileIter, &socketList); - printf("\nClient has closed connection.\n"); - } - else - { - // if it is open, send the message back to the client over the client socket - send(fileIter, message, messageBytes, 0); - printf("\nMessage has been sent back to client.\n"); - } - } - } - } - } - // close the socket - close(socketEchoFD); - // profit??? - return 0; -} - -// function definitions - -/* -Name: ??? -Process: ??? -Function Input/Parameters: ??? -Function Output/Parameters: ??? -Function Output/Returned: ??? -Device Input/Device: ??? -Device Output/Device: ??? -Dependencies: ??? -*/ \ No newline at end of file diff --git a/echoserver/echoserver.h b/echoserver/echoserver.h deleted file mode 100644 index b1ef31c..0000000 --- a/echoserver/echoserver.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef ECHOSERVER -#define ECHOSERVER - -// included libraries -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// data structures - -// function prototypes - -#endif // ECHOSERVER \ No newline at end of file diff --git a/timeserver/Makefile b/timeserver/Makefile deleted file mode 100644 index 6f7d8b7..0000000 --- a/timeserver/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -CC = gcc -Wall -std=c11 -pedantic -SOURCES = timeserver.c -OBJECTS = timeserver.o - -all: timeserver - -timeserver: $(OBJECTS) - $(CC) $(OBJECTS) -o timeserver - -timeserver.o: timeserver.c - $(CC) -c timeserver.c -o timeserver.o - -clean: - rm -f $(OBJECTS) timeserver timeserver.out - -clean_objects: - rm -f $(OBJECTS) - -run: - ./timeserver diff --git a/timeserver/README.md b/timeserver/README.md deleted file mode 100644 index b9d8b6a..0000000 --- a/timeserver/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Running the timeserver -Use `make` to compile the timeserver. -Use `make run` to run the timeserver. This will run on an infinite loop. -Use `Ctrl+C` to stop the timeserver. - -## timeserver communication -Communicating with the server can happen using netcat in UDP mode (-u flag). - -## timeserver information -The timeserver runs on UDP port 8888. -The timeserver will wait for `time\r\n` to be sent on this port, and if received, -will send back a response of the time (in ms) since January 1st, 1970 (UNIX Epoch). - -## pcap information -A demonstration of the timeserver has been captured with Wireshark in the -included `demo.pcap` file. \ No newline at end of file diff --git a/timeserver/demo.pcap b/timeserver/demo.pcap deleted file mode 100644 index 6c48bde..0000000 Binary files a/timeserver/demo.pcap and /dev/null differ diff --git a/timeserver/timeserver.c b/timeserver/timeserver.c deleted file mode 100644 index 6eef908..0000000 --- a/timeserver/timeserver.c +++ /dev/null @@ -1,83 +0,0 @@ -// header files -#include "timeserver.h" - -// constants -// timeserver port is 8888 -#define TIMEPORT 8888 -#define BUFFER_SIZE 1024 - -// prototypes - -// main function -int main(int argc, char **argv) -{ - // initialize program (variables and structs) - bool quitFlag = true; - int socketTimeFD; - char message[BUFFER_SIZE]; - struct sockaddr_in serverAddr, clientAddr; - memset(&serverAddr, 0, sizeof(serverAddr)); - memset(&clientAddr, 0, sizeof(clientAddr)); - // define a UDP socket - socketTimeFD = socket(AF_INET, SOCK_DGRAM, 0); - if(socketTimeFD < 0) - { - printf("Unable to create the socket."); - return -1; - } - // bind the UDP socket to port 8888 / TIMEPORT - serverAddr.sin_family = AF_INET; - serverAddr.sin_addr.s_addr = INADDR_ANY; - serverAddr.sin_port = htons(TIMEPORT); - int bindCheck = bind(socketTimeFD, &serverAddr, sizeof(serverAddr)); - if(bindCheck < 0) - { - printf("Unable to bind the socket to the port or address.\n Is either in use?"); - return -1; - } - // while loop to see if the "time\r\n" has been received (recv call?) - printf("Timeserver listening on port: %d\n", TIMEPORT); - while(quitFlag) - { - int clientAddrLen; - int receivedDataLen; - // grab the data from the client - receivedDataLen = recvfrom(socketTimeFD, message, BUFFER_SIZE, MSG_WAITALL, &clientAddr, &clientAddrLen); - message[receivedDataLen] = '\0'; - // if "time\r\n" is received, then send() back the linux epoch. - if(strcmp(message, "time\n") == 0 || strcmp(message, "time\r\n") == 0) - { - // setup the reply message - char reply[BUFFER_SIZE]; - // add the current time to the reply - sprintf(reply, "%d", time(NULL)); - // send the reply to the requestee - sendto(socketTimeFD, (const char *)reply, strlen(reply), 0, (const struct sockaddr *) &clientAddr, clientAddrLen); - // log the time command recv'd - printf("\nServer received time. Replied with: %d\n", time(NULL)); - } - else if (strcmp(message, "close\n") == 0 || strcmp(message, "quit\n") == 0 || strcmp(message, "q\n") == 0) - { - // if the server recv'd the close command, then close the server. - printf("\nServer received close.\n"); - quitFlag = false; - } - } - // close the socket - close(socketTimeFD); - // profit??? - return 0; -} - -// function definitions - -/* -Name: ??? -Process: ??? -Function Input/Parameters: ??? -Function Output/Parameters: ??? -Function Output/Returned: ??? -Device Input/Device: ??? -Device Output/Device: ??? -Dependencies: ??? -*/ \ No newline at end of file diff --git a/timeserver/timeserver.h b/timeserver/timeserver.h deleted file mode 100644 index 1a8135b..0000000 --- a/timeserver/timeserver.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef TIMESERVER -#define TIMESERVER - -// included libraries -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// data structures - -// function prototypes - -#endif // TIMESERVER \ No newline at end of file