diff --git a/.env b/.env index c7ab229..577ff2b 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -SERVER_IP=172.30.0.39 -SERVER_PORT=3030 -CLIENT_IP=172.30.0.40 -NET_SUBNET= \ No newline at end of file +NET_SUBNET=172.30.0.0/24 +SERVER_IP=172.30.0.10 +CLIENT_IP=172.30.0.11 +SERVER_PORT=3030 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ddd9f5a --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# latex garbages +*.aux +*.fdb_latexmk +*.fls +*.log +*.synctex.gz + +# vscode garbage +.vscode \ No newline at end of file diff --git a/H0_CSE4303_ZheyuanWu.tex b/H0_CSE4303_ZheyuanWu.tex deleted file mode 100644 index 6c0ee9c..0000000 --- a/H0_CSE4303_ZheyuanWu.tex +++ /dev/null @@ -1,97 +0,0 @@ -\documentclass[11pt]{article} -\usepackage{amsmath, amsfonts, amsthm} -\usepackage{amssymb} -\usepackage{fancyhdr,parskip} -\usepackage{fullpage} -\usepackage{mathrsfs} -\usepackage{mathtools} - -%% -%% Stuff above here is packages that will be used to compile your document. -%% If you've used unusual LaTeX features, you may have to install extra packages by adding them to this list. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -\setlength{\headheight}{15.2pt} -\setlength{\headsep}{20pt} -\pagestyle{fancyplain} - -%% -%% Stuff above here is layout and formatting. If you've never used LaTeX before, you probably don't need to change any of it. -%% Later, you can learn how it all works and adjust it to your liking, or write your own formatting code. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%%%%%%%%%%%%%%%%%%%%%% -% These commands create theorem-like environments. -\newtheorem{theorem}{Theorem} -\newtheorem{lemma}[theorem]{Lemma} -\newtheorem{corollary}[theorem]{Corollary} -\newtheorem{prop}[theorem]{Proposition} -\newtheorem{defn}[theorem]{Definition} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This section contains some useful macros that will save you time typing. -%% - -% Using \displaystyle (or \ds) in a block of math has a number of effects, but most notably, it makes your fractions come out bigger. -\newcommand{\ds}{\displaystyle} - -% These lines are for displaying integrals; typing \dx will make the dx at the end of the integral look better. -\newcommand{\is}{\hspace{2pt}} -\newcommand{\dx}{\is dx} - -% These commands produce the fancy Z (for the integers) and other letters conveniently. -\newcommand{\Z}{\mathbb{Z}} -\newcommand{\Q}{\mathbb{Q}} -\newcommand{\R}{\mathbb{R}} -\newcommand{\C}{\mathbb{C}} -\newcommand{\F}{\mathbb{F}} -\newcommand{\T}{\mathcal{T}} -\newcommand{\B}{\mathcal{B}} - -% for fancy empty set char -\renewcommand{\emptyset}{\varnothing} - -% customized commands for future assignements -\newcommand{\imply}{\Rightarrow} -\def\P{\mathscr{P}} -\def\L{\mathscr{L}} -\def\M{\mathscr{M}} -\DeclarePairedDelimiterX{\inp}[2]{\langle}{\rangle}{#1, #2} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% This is the header. It will appear on every page, and it's a good place to put your name, the assignment title, and stuff like that. -%% I usually leave the center header blank to avoid clutter. -%% - -\fancyhead[L]{\textbf{CSE4303 Homework 0}} -\fancyhead[C]{509191} -\fancyhead[R]{Zheyuan Wu} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -\begin{document} - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Actual math starts here! - - -% Use an enumerated list to write up problems. First we begin a list. - -\begin{enumerate} - -\item[1.] - - -\end{enumerate} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Actual math ends here. Don't put any content below the \end{document} line. -%% - -\end{document} diff --git a/bin/client b/bin/client new file mode 100644 index 0000000..fc848db Binary files /dev/null and b/bin/client differ diff --git a/bin/server b/bin/server new file mode 100644 index 0000000..a2b20ff Binary files /dev/null and b/bin/server differ diff --git a/client-log.sh b/client-log.sh new file mode 100644 index 0000000..d3c2268 --- /dev/null +++ b/client-log.sh @@ -0,0 +1 @@ +docker logs hw1-client \ No newline at end of file diff --git a/client-term.sh b/client-term.sh new file mode 100644 index 0000000..c967000 --- /dev/null +++ b/client-term.sh @@ -0,0 +1 @@ +docker exec -it hw1-client sh diff --git a/client.cpp b/client.cpp index e2855e2..efd5c1a 100644 --- a/client.cpp +++ b/client.cpp @@ -3,7 +3,9 @@ #include #include #include +#include // for close() +// load environment variables from .env file #include #include @@ -18,38 +20,87 @@ void load_env(const std::string& path) { std::string key = line.substr(0, pos); std::string val = line.substr(pos + 1); -#ifdef _WIN32 - _putenv_s(key.c_str(), val.c_str()); -#else - setenv(key.c_str(), val.c_str(), 1); -#endif + #ifdef _WIN32 + _putenv_s(key.c_str(), val.c_str()); + #else + setenv(key.c_str(), val.c_str(), 1); + #endif } } -namespace std {} int main(void){ load_env(".env"); // Declare variables const char *server_ip = std::getenv("SERVER_IP"); const int server_port = std::atoi(std::getenv("SERVER_PORT")); + printf("Connecting to server %s:%d\n", server_ip, server_port); + char client_message[1024]; char server_message[1024]; + const char * custom_message="Zheyuan Wu: "; + // Create socket: + int client_socket = socket(AF_INET, SOCK_STREAM, 0); + if (client_socket == -1) { + perror("Failed to create socket"); + return 1; + }else{ + printf("Socket created successfully\n"); + } // Send connection request to server, be sure to set por tand IP the same as server-side + struct sockaddr_in server_addr; + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(server_port); + inet_pton(AF_INET, server_ip, &server_addr.sin_addr); + + if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { + perror("Failed to connect to server"); + close(client_socket); + return 1; + }else{ + printf("Connected to server successfully\n"); + } // Get input from the user: - printf("Enter message sent to the server: "); - // fgets(client_message); + printf("Enter message sent to the server (type \\quit to exit): "); + // clean the buffer + memset(client_message, 0, sizeof(client_message)); + if (fgets(client_message, sizeof(client_message), stdin) == NULL) { + // EOF or error reading from stdin, exit the loop + perror("Error reading from stdin"); + return 1; + } + + while (strcmp(client_message, "\\quit\n") != 0) { + + // Send the message to server: + // add my name in the front + char buffer[2048]; + std::snprintf(buffer, sizeof(buffer), "%s%s", custom_message, client_message); + send(client_socket, buffer, sizeof(buffer), 0); - // Send the message to server: - - // Receive the server's response: + // Receive the server's response: + recv(client_socket, server_message, sizeof(server_message), 0); - printf("Server's response: %s\n", server_message); + printf("Server's response: %s\n", server_message); + + printf("Enter message sent to the server (type \\quit to exit): "); + + // clean the buffer + memset(client_message, 0, sizeof(client_message)); + memset(server_message, 0, sizeof(server_message)); + + if (fgets(client_message, sizeof(client_message), stdin) == NULL) { + // EOF or error reading from stdin, exit the loop + perror("Error reading from stdin"); + break; + } + } // Close the socket + close(client_socket); return 0; } \ No newline at end of file diff --git a/compile.sh b/compile.sh new file mode 100644 index 0000000..90c74c8 --- /dev/null +++ b/compile.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +mkdir -p bin + +# Adjust flags as you like +CXXFLAGS="-std=c++17 -O2 -Wall -Wextra -pedantic" + +g++ ${CXXFLAGS} server.cpp -o bin/server +g++ ${CXXFLAGS} client.cpp -o bin/client + +echo "Built:" +file bin/server bin/client || true + +chmod a+x bin/server bin/client + +docker compose up -d server +docker compose run --rm -it client + +echo "Done." \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 2683ba5..28e7489 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,27 +1,21 @@ services: server: - image: gcc:13 + image: debian:bookworm-slim + container_name: hw1-server working_dir: /app - volumes: - - ./:/app:rw env_file: - .env networks: net-hw1: ipv4_address: ${SERVER_IP} - command: > - bash -lc " - set -euo pipefail; - g++ -std=c++17 -O2 -Wall -Wextra -pedantic /app/server.cpp -o /tmp/server; - echo '[server] IP='$(hostname -i)' SERVER_IP='${SERVER_IP}' SERVER_PORT='${APP_PORT}; - exec /tmp/server - " + volumes: + - ./bin/server:/usr/local/bin/server:ro + entrypoint: ["bash", "-lc", "apt-get update && apt-get install -y --no-install-recommends libstdc++6 iproute2; exec stdbuf -oL -eL /usr/local/bin/server"] client: - image: gcc:13 + image: debian:bookworm-slim + container_name: hw1-client working_dir: /app - volumes: - - ./:/app:rw env_file: - .env networks: @@ -31,13 +25,9 @@ services: - server stdin_open: true tty: true - command: > - bash -lc " - set -euo pipefail; - g++ -std=c++17 -O2 -Wall -Wextra -pedantic /app/client.cpp -o /tmp/client; - echo '[client] IP='$(hostname -i)' CLIENT_IP='${CLIENT_IP}' SERVER_IP='${SERVER_IP}' APP_PORT='${APP_PORT}; - exec /tmp/client - " + volumes: + - ./bin/client:/usr/local/bin/client:ro + entrypoint: ["bash", "-lc", "apt-get update && apt-get install -y --no-install-recommends libstdc++6 iproute2; exec stdbuf -oL -eL /usr/local/bin/client"] networks: net-hw1: diff --git a/latex/H0_CSE4303_ZheyuanWu.pdf b/latex/H0_CSE4303_ZheyuanWu.pdf new file mode 100644 index 0000000..27d6af0 Binary files /dev/null and b/latex/H0_CSE4303_ZheyuanWu.pdf differ diff --git a/latex/H0_CSE4303_ZheyuanWu.tex b/latex/H0_CSE4303_ZheyuanWu.tex new file mode 100644 index 0000000..1eef884 --- /dev/null +++ b/latex/H0_CSE4303_ZheyuanWu.tex @@ -0,0 +1,477 @@ +\documentclass[11pt]{article} +\usepackage{amsmath, amsfonts, amsthm} +\usepackage{amssymb} +\usepackage{fancyhdr,parskip} +\usepackage{fullpage} +\usepackage{mathrsfs} +\usepackage{mathtools} + +\usepackage{float} + +% code listing +\usepackage{listings} +\usepackage{xcolor} +\lstset{% + language=c++, + breaklines=true, + commentstyle=\color{green}, % comment style + escapeinside={\%*}{*)}, % if you want to add LaTeX within your code + keywordstyle=\color{blue}, % keyword style + stringstyle=\color{purple}, % string literal style +} + +%% +%% Stuff above here is packages that will be used to compile your document. +%% If you've used unusual LaTeX features, you may have to install extra packages by adding them to this list. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\setlength{\headheight}{15.2pt} +\setlength{\headsep}{20pt} +\pagestyle{fancyplain} + +%% +%% Stuff above here is layout and formatting. If you've never used LaTeX before, you probably don't need to change any of it. +%% Later, you can learn how it all works and adjust it to your liking, or write your own formatting code. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%% +% These commands create theorem-like environments. +\newtheorem{theorem}{Theorem} +\newtheorem{lemma}[theorem]{Lemma} +\newtheorem{corollary}[theorem]{Corollary} +\newtheorem{prop}[theorem]{Proposition} +\newtheorem{defn}[theorem]{Definition} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This section contains some useful macros that will save you time typing. +%% + +% Using \displaystyle (or \ds) in a block of math has a number of effects, but most notably, it makes your fractions come out bigger. +\newcommand{\ds}{\displaystyle} + +% These lines are for displaying integrals; typing \dx will make the dx at the end of the integral look better. +\newcommand{\is}{\hspace{2pt}} +\newcommand{\dx}{\is dx} + +% These commands produce the fancy Z (for the integers) and other letters conveniently. +\newcommand{\Z}{\mathbb{Z}} +\newcommand{\Q}{\mathbb{Q}} +\newcommand{\R}{\mathbb{R}} +\newcommand{\C}{\mathbb{C}} +\newcommand{\F}{\mathbb{F}} +\newcommand{\T}{\mathcal{T}} +\newcommand{\B}{\mathcal{B}} + +% for fancy empty set char +\renewcommand{\emptyset}{\varnothing} + +% customized commands for future assignements +\newcommand{\imply}{\Rightarrow} +\def\P{\mathscr{P}} +\def\L{\mathscr{L}} +\def\M{\mathscr{M}} +\DeclarePairedDelimiterX{\inp}[2]{\langle}{\rangle}{#1, #2} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This is the header. It will appear on every page, and it's a good place to put your name, the assignment title, and stuff like that. +%% I usually leave the center header blank to avoid clutter. +%% + +\fancyhead[L]{\textbf{CSE4303 Homework 0}} +\fancyhead[C]{509191} +\fancyhead[R]{Zheyuan Wu} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\begin{document} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Actual math starts here! + + +% Use an enumerated list to write up problems. First we begin a list. + +\begin{enumerate} + +\item[1.] Server code + +\begin{enumerate} + \item Screenshot or well-formatted copy of code + + \begin{lstlisting}[ + language=C++, + breaklines=true + ] +#include +#include +#include +#include +#include // for perror() +#include // for close() + +// load environment variables from .env file +#include +#include + +void load_env(const std::string& path) { + std::ifstream f(path); + std::string line; + while (std::getline(f, line)) { + if (line.empty() || line[0] == '#') continue; + auto pos = line.find('='); + if (pos == std::string::npos) continue; + + std::string key = line.substr(0, pos); + std::string val = line.substr(pos + 1); + + #ifdef _WIN32 + _putenv_s(key.c_str(), val.c_str()); + #else + setenv(key.c_str(), val.c_str(), 1); + #endif + } +} + +int main(void){ + printf("Server starting...\n"); + load_env(".env"); + + // Declare variables + const char *server_ip = std::getenv("SERVER_IP"); + const int server_port = std::atoi(std::getenv("SERVER_PORT")); + char client_message[2048]; + char server_message[2048]; + const char * custom_message="Server: Hello from server, "; + + // debug + printf("Server starting at IP: %s, Port: %d\n", server_ip, server_port); + + // Create socket + const int server_socket = socket(AF_INET, SOCK_STREAM, 0); + if (server_socket == -1) { + perror("Failed to create socket"); + return 1; + } + + // Bind to the set port and IP + struct sockaddr_in server_addr; + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(server_port); + inet_pton(AF_INET, server_ip, &server_addr.sin_addr); + + if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { + perror("Failed to bind socket"); + close(server_socket); + return 1; + } + printf("Done with binding with IP: %s, Port: %d\n", server_ip, server_port); + + // Listen for clients: + const char *client_ip = std::getenv("CLIENT_IP"); + if (listen(server_socket, 1) == -1) { + perror("Failed to listen on socket"); + close(server_socket); + return 1; + } + printf("Listening for incoming connections...\n"); + + // Accept an incoming connection + struct sockaddr_in client_addr; + socklen_t client_addr_len = sizeof(client_addr); + int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_len); + if (client_socket == -1) { + perror("Failed to accept connection"); + close(server_socket); + return 1; + } + printf("Client connected at IP: %s \n", client_ip); + + // Receive client's message + + // clean exising buffer + memset(client_message, 0, sizeof(client_message)); + + recv(client_socket, client_message, sizeof(client_message), 0); + printf("Msg from client: %s", client_message); + + // Respond to client + // prepare server message + memset(server_message, 0, sizeof(server_message)); + ; + + // add my name in the back as response. + std::snprintf(server_message, sizeof(server_message), "%s%s", custom_message, strtok(client_message, ":")); + if (send(client_socket, server_message, sizeof(server_message), 0) == -1) { + perror("Failed to send message"); + close(client_socket); + close(server_socket); + return 1; + } + printf("Response sent to client: %s\n", server_message); + + // Close the socket + close(client_socket); + close(server_socket); + + return 0; +} + \end{lstlisting} + \item Quick overview of what the code does in your own words + + First, I created an environment file to ensure that the ip address from both cpp file gets the same ip address. I defined the ip as follows: + + \begin{lstlisting}[ + language=bash, + breaklines=true + ] + NET_SUBNET=172.30.0.0/24 + SERVER_IP=172.30.0.10 + CLIENT_IP=172.30.0.11 + SERVER_PORT=3030 + \end{lstlisting} + + Secondly, I made \texttt{load\_env} function by modifying the scripts from the internet. + + After that, in main function, we set the variables server ip, server port, client message buffer and server message buffer. + + Then, we created a socket, bind it to the set port and IP. If then everything goes well, we listen for incoming connections from client side and retrieve client ip. After that, we accept an incoming connection, receive client's message, and respond to client using the client name defined in client code and concat with our custom response defined before. + + The server will quit automatically when we response 1 message from client. + +\end{enumerate} + +\newpage + +\item[2.] Client code + +\begin{enumerate} + \item Screenshot or well-formatted copy of code + \begin{lstlisting}[ + language=C++, + breaklines=true + ] + #include +#include +#include +#include +#include +#include // for close() + +// load environment variables from .env file +#include +#include + +void load_env(const std::string& path) { + std::ifstream f(path); + std::string line; + while (std::getline(f, line)) { + if (line.empty() || line[0] == '#') continue; + auto pos = line.find('='); + if (pos == std::string::npos) continue; + + std::string key = line.substr(0, pos); + std::string val = line.substr(pos + 1); + + #ifdef _WIN32 + _putenv_s(key.c_str(), val.c_str()); + #else + setenv(key.c_str(), val.c_str(), 1); + #endif + } +} + +int main(void){ + load_env(".env"); + // Declare variables + const char *server_ip = std::getenv("SERVER_IP"); + const int server_port = std::atoi(std::getenv("SERVER_PORT")); + printf("Connecting to server %s:%d\n", server_ip, server_port); + + char client_message[1024]; + char server_message[1024]; + const char * custom_message="Zheyuan Wu: "; + + // Create socket: + int client_socket = socket(AF_INET, SOCK_STREAM, 0); + if (client_socket == -1) { + perror("Failed to create socket"); + return 1; + }else{ + printf("Socket created successfully\n"); + } + + // Send connection request to server, be sure to set por tand IP the same as server-side + struct sockaddr_in server_addr; + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(server_port); + inet_pton(AF_INET, server_ip, &server_addr.sin_addr); + + if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { + perror("Failed to connect to server"); + close(client_socket); + return 1; + }else{ + printf("Connected to server successfully\n"); + } + + // Get input from the user: + printf("Enter message sent to the server (type \\quit to exit): "); + // clean the buffer + memset(client_message, 0, sizeof(client_message)); + if (fgets(client_message, sizeof(client_message), stdin) == NULL) { + // EOF or error reading from stdin, exit the loop + perror("Error reading from stdin"); + return 1; + } + + while (strcmp(client_message, "\\quit\n") != 0) { + + // Send the message to server: + // add my name in the front + char buffer[2048]; + std::snprintf(buffer, sizeof(buffer), "%s%s", custom_message, client_message); + send(client_socket, buffer, sizeof(buffer), 0); + + // Receive the server's response: + recv(client_socket, server_message, sizeof(server_message), 0); + + + printf("Server's response: %s\n", server_message); + + printf("Enter message sent to the server (type \\quit to exit): "); + + // clean the buffer + memset(client_message, 0, sizeof(client_message)); + memset(server_message, 0, sizeof(server_message)); + + if (fgets(client_message, sizeof(client_message), stdin) == NULL) { + // EOF or error reading from stdin, exit the loop + perror("Error reading from stdin"); + break; + } + } + + // Close the socket + close(client_socket); + + return 0; +} + \end{lstlisting} + \item The message you send from the client to the server should include your name! + + We use \texttt{custom\_message} to ensure that each message we send to the server starts with our name. + + \item Quick overview of what the code does in your own words. + + First we use the same function defined in \texttt{server.cpp} to load the environment file to ensure that the server and client gets the same ip address so I don't need to check it anymore. + + Secondly, we build sockets that connects to server by address and port provided. If everything goes well, we will ask for user to input the message they like and send them to server with my name defined in \texttt{custom\_message}. Then concat the string to the buffer and send them to the server. + + After that we listen for response from the server and print it and wait for user to input the next message. + + The server will quit automatically when we type \texttt{\\quit}. + +\end{enumerate} + +\newpage +\item[3.] Testing + +\begin{enumerate} + \item Provide and overview of the setup you used to test your client/server program. You should not run them on the same host (same machine/simulated machine. Each machine should have a unique IP, use the Sniffing and Snooping SeedLab setup). + + I use docker compose to run the server and client on separate containers with independent IP addresses. + + The docker compose file is provided as follows and env is defined in before. + + \begin{lstlisting}[ + language=bash, + breaklines=true + ] +services: + server: + image: debian:bookworm-slim + container_name: hw1-server + working_dir: /app + env_file: + - .env + networks: + net-hw1: + ipv4_address: ${SERVER_IP} + volumes: + - ./bin/server:/usr/local/bin/server:ro + entrypoint: ["bash", "-lc", "apt-get update && apt-get install -y --no-install-recommends libstdc++6 iproute2; exec stdbuf -oL -eL /usr/local/bin/server"] + + client: + image: debian:bookworm-slim + container_name: hw1-client + working_dir: /app + env_file: + - .env + networks: + net-hw1: + ipv4_address: ${CLIENT_IP} + depends_on: + - server + stdin_open: true + tty: true + volumes: + - ./bin/client:/usr/local/bin/client:ro + entrypoint: ["bash", "-lc", "apt-get update && apt-get install -y --no-install-recommends libstdc++6 iproute2; exec stdbuf -oL -eL /usr/local/bin/client"] + +networks: + net-hw1: + name: net-hw1 + driver: bridge + ipam: + config: + - subnet: ${NET_SUBNET} +\end{lstlisting} + + \item Provide screenshots showing your test in action + \begin{enumerate} + \item Likely at least one of the server receiving the connection and message + \begin{figure}[h] + \centering + \includegraphics[width=0.8\textwidth]{./images/server.png} + \end{figure} + \item Likely at least one of the client receiving and printing the return message from the server + \begin{figure}[h] + \centering + \includegraphics[width=0.8\textwidth]{./images/client.png} + \end{figure} + \end{enumerate} +\end{enumerate} + +\newpage + +\item[4.] Extra credit +\begin{enumerate} + \item[a.] Run your client and server on the HostA and HostB on the SeedVM docker setup for studio 1. Prior to running the client/server, use the SeedAttacker VM to run wireshark and sniff all packets on the network. Use Wireshark to trace the TCP connection from client to server and find the packets containing the message sent from client to server. Is an eavesdropper able to obtain the contents of the message? Include a section in your report for extra credit. Document the work you did and include at least one screenshot showing the Wireshark packet capture. In this screenshot, make sure one of the packets containing the client message is the actively clicked on packet and the message/part of the message is visible in the detailed packet window. + + Here is the screenshot of wireshark packet capture for client message + + \begin{figure}[h] + \centering + \includegraphics[width=0.8\textwidth]{images/wireshark_client.png} + \end{figure} + + Here is the screenshot of wireshark packet capture for server message + + \begin{figure}[h] + \centering + \includegraphics[width=0.8\textwidth]{images/wireshark_client.png} + \end{figure} +\end{enumerate} +\end{enumerate} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Actual math ends here. Don't put any content below the \end{document} line. +%% + +\end{document} diff --git a/latex/images/client.png b/latex/images/client.png new file mode 100644 index 0000000..d67cdbc Binary files /dev/null and b/latex/images/client.png differ diff --git a/latex/images/server.png b/latex/images/server.png new file mode 100644 index 0000000..39c1b89 Binary files /dev/null and b/latex/images/server.png differ diff --git a/latex/images/wireshark_client.png b/latex/images/wireshark_client.png new file mode 100644 index 0000000..d947790 Binary files /dev/null and b/latex/images/wireshark_client.png differ diff --git a/latex/images/wireshark_server.png b/latex/images/wireshark_server.png new file mode 100644 index 0000000..be9231b Binary files /dev/null and b/latex/images/wireshark_server.png differ diff --git a/server b/server deleted file mode 100755 index 98777c0..0000000 Binary files a/server and /dev/null differ diff --git a/server-log.sh b/server-log.sh new file mode 100644 index 0000000..0cd26a5 --- /dev/null +++ b/server-log.sh @@ -0,0 +1 @@ +docker logs hw1-server --tail=50 \ No newline at end of file diff --git a/server.cpp b/server.cpp index 12965ab..e896a57 100644 --- a/server.cpp +++ b/server.cpp @@ -2,35 +2,112 @@ #include #include #include -#include +#include // for perror() +#include // for close() + +// load environment variables from .env file +#include +#include + +void load_env(const std::string& path) { + std::ifstream f(path); + std::string line; + while (std::getline(f, line)) { + if (line.empty() || line[0] == '#') continue; + auto pos = line.find('='); + if (pos == std::string::npos) continue; + + std::string key = line.substr(0, pos); + std::string val = line.substr(pos + 1); + + #ifdef _WIN32 + _putenv_s(key.c_str(), val.c_str()); + #else + setenv(key.c_str(), val.c_str(), 1); + #endif + } +} int main(void){ + printf("Server starting...\n"); + load_env(".env"); // Declare variables - const char *server_ip = "127.0.0.1"; - const int server_port = 8080; - char client_message[1024]; - char server_message[1024]; + const char *server_ip = std::getenv("SERVER_IP"); + const int server_port = std::atoi(std::getenv("SERVER_PORT")); + char client_message[2048]; + char server_message[2048]; + const char * custom_message="Server: Hello from server, "; + + // debug + printf("Server starting at IP: %s, Port: %d\n", server_ip, server_port); + // Create socket - + const int server_socket = socket(AF_INET, SOCK_STREAM, 0); + if (server_socket == -1) { + perror("Failed to create socket"); + return 1; + } // Bind to the set port and IP + struct sockaddr_in server_addr; + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(server_port); + inet_pton(AF_INET, server_ip, &server_addr.sin_addr); + + if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { + perror("Failed to bind socket"); + close(server_socket); + return 1; + } printf("Done with binding with IP: %s, Port: %d\n", server_ip, server_port); // Listen for clients: - const char *client_ip = "127.0.0.1"; - const int client_port = 12345; + const char *client_ip = std::getenv("CLIENT_IP"); + if (listen(server_socket, 1) == -1) { + perror("Failed to listen on socket"); + close(server_socket); + return 1; + } + printf("Listening for incoming connections...\n"); // Accept an incoming connection - printf("Client connected at IP: %s and port: %i\n", client_ip, client_port); + struct sockaddr_in client_addr; + socklen_t client_addr_len = sizeof(client_addr); + int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_len); + if (client_socket == -1) { + perror("Failed to accept connection"); + close(server_socket); + return 1; + } + printf("Client connected at IP: %s \n", client_ip); // Receive client's message - printf("Msg from client: %s\n", client_message); + + // clean exising buffer + memset(client_message, 0, sizeof(client_message)); + + recv(client_socket, client_message, sizeof(client_message), 0); + printf("Msg from client: %s", client_message); // Respond to client - strcpy(server_message, "Hello from server"); + // prepare server message + memset(server_message, 0, sizeof(server_message)); + ; + + // add my name in the back as response. + std::snprintf(server_message, sizeof(server_message), "%s%s", custom_message, strtok(client_message, ":")); + if (send(client_socket, server_message, sizeof(server_message), 0) == -1) { + perror("Failed to send message"); + close(client_socket); + close(server_socket); + return 1; + } + printf("Response sent to client: %s\n", server_message); // Close the socket + close(client_socket); + close(server_socket); return 0; } \ No newline at end of file