From c150cb1e4e39abe749133733fd7e9721506740e8 Mon Sep 17 00:00:00 2001
From: Santiago Lo Coco <slococo@itba.edu.ar>
Date: Sun, 7 Nov 2021 22:04:16 -0300
Subject: [PATCH] Make lots of improvements (and add extra features)

Co-authored-by: Ezequiel Bellver <ebellver@itba.edu.ar>
Co-authored-by: Juan Barmasch <jbarmasch@itba.edu.ar>
---
 Makefile                |  9 ++++----
 README.md               | 26 +++++++++++++++++++--
 challenges.c            | 26 +++++++++++++++------
 client.c                | 35 ++++++++++++++++++++++++----
 errors.c                |  3 +--
 include/challenges.h    |  5 ++++
 include/challengesLib.h | 51 +++++++++++++++++++++++++++++++++++++++++
 include/client.h        |  2 ++
 include/random.h        |  8 +++++++
 include/server.h        | 34 +--------------------------
 quine.c                 |  2 --
 random.c                | 17 ++++++++++++++
 server.c                | 44 ++++++++++++++++++++++++++++++-----
 13 files changed, 202 insertions(+), 60 deletions(-)
 create mode 100644 include/challengesLib.h
 create mode 100644 include/random.h
 create mode 100644 random.c

diff --git a/Makefile b/Makefile
index 3a9ef3d..6bff332 100644
--- a/Makefile
+++ b/Makefile
@@ -5,13 +5,14 @@ CLIENT=client
 SERVER=server
 ERROR=errors.o
 CHALLENGE=challenges.o
+RANDOM=random.o
 
 TMP := $(shell mktemp)
 
-all: $(ERROR) $(CHALLENGE) $(CLIENT) $(SERVER)
+all: $(ERROR) $(CHALLENGE) $(RANDOM) $(CLIENT) $(SERVER)
 
-$(SERVER): server.c challenges.c errors.c include/errors.h include/challenges.h include/server.h
-	$(CC) $(CCFLAGS) server.c -o server challenges.o errors.o
+$(SERVER): server.c challenges.c errors.c include/errors.h include/challenges.h include/server.h include/challengesLib.h
+	$(CC) $(CCFLAGS) server.c -o server challenges.o errors.o random.o -lm
 	objcopy --add-section .RUN_ME="$(TMP)" --set-section-flags .mydata=noload,readonly server
 	strip --strip-debug server
 	rm "$(TMP)"
@@ -23,7 +24,7 @@ $(CLIENT): client.c errors.c include/errors.h include/client.h
 	$(CC) $(CCFLAGS) -I./include -c $<
 
 clean:
-	rm -rf $(ERROR) $(CHALLENGE) $(CLIENT) $(SERVER)
+	rm -rf $(ERROR) $(CHALLENGE) $(CLIENT) $(SERVER) $(RANDOM)
 
 test:
 	pvs-studio-analyzer trace -- make
diff --git a/README.md b/README.md
index 7b3744a..0406afd 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,19 @@ make all
 
 ## Ejecución <a name="ejecución"></a>
 
-Ahora, tendrá dos ejecutables: `client` y `server`.
+Ahora, tendrá dos ejecutables: `client` y `server`. Note que primero debe correr el `server` y luego conectarse con el `client`.
+
+```bash
+./server
+```
+
+y en otra terminal
+
+```bash
+./client
+```
+
+Debe notar que el `server` usará el puerto 8080 de localhost por lo que si tiene algún otro servidor en ese puerto debe cerrarlo antes de correrlo.
 
 ## Testeos <a name="tests"></a>
 
@@ -37,7 +49,17 @@ En orden de realizar un análisis estático del sistema usted debe tener instala
 make test
 ```
 
-Por último, si quiere hacer un análisis dinámico (usando [valgrind](https://valgrind.org/)) debe...
+Por último, si quiere hacer un análisis dinámico (usando [valgrind](https://valgrind.org/)) puede hacerlo mediante:
+
+```bash
+valgrind ./server
+```
+
+y en otra terminal
+
+```bash
+valgrind ./client
+```
 
 # Autores
 - Barmasch, Juan Martín (61033)
diff --git a/challenges.c b/challenges.c
index cb9e3ed..bcc116e 100644
--- a/challenges.c
+++ b/challenges.c
@@ -2,8 +2,6 @@
 // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 #include "include/challenges.h"
 
-char too_easy = 'a';
-
 char genChallenge(FILE * stream, char ** output, challenge_t challenge) {
     printf("------------- DESAFIO -------------\n");
     printf("%s\n\n", challenge.message);
@@ -19,7 +17,6 @@ char genChallenge(FILE * stream, char ** output, challenge_t challenge) {
         printSystemError("Challenges: getline()");
 
     int ans = strcmp(*output, challenge.flag);
-    // free(*output);
     return !ans;
 }
 
@@ -32,14 +29,14 @@ void filterChallenge() {
     char * ans = "La respuesta es K5n2UFfpFMUN\n";
     int i = 0;
     while (ans[i] != '\0') {
-        int fdRandom = (rand() % (UPPER - LOWER + 1)) + LOWER;
-        if (fdRandom == STDOUT_FILENO) {
+        double fdRandom = getUniform(1);
+        if (fdRandom <= 0.25) {
             if (write(STDOUT_FILENO, ans + i++, 1) < 0)
                 printSystemError("Challenges: write()");
         }
         else {
-            for (int i = (rand() % (6)) + 1; i > 0; i--) {
-                int randNum = 90 * (rand() / (RAND_MAX + 1.0));
+            for (int j = (int) getUniform(3) + 1; j > 0; j--) {
+                int randNum = (int) getUniform(90);
                 char randLetter = (char) (randNum + '#');
                 if (write(STDERR_FILENO, &randLetter, 1) < 0)
                     printSystemError("Challenges: write()");
@@ -95,4 +92,19 @@ void gdbChallenge() {
         printf("La respuesta es gdb_rules\n");
     else
         printf("ENTER para reintentar.\n");
+}
+
+void boxMuller(double * normal) {
+    double random1 = getUniform(1);
+    double random2 = getUniform(1);
+    *normal = sqrt(-2 * log(random1)) * cos(2 * M_PI * random2);
+}
+
+void normalChallenge() {
+    for (int i = 0; i < MAX_NORMALS; i++) {
+        double normal = 0;
+        boxMuller(&normal);
+        printf("%.6f ", normal);
+    }
+    printf("\n");
 }
\ No newline at end of file
diff --git a/client.c b/client.c
index cac786e..46cb63f 100644
--- a/client.c
+++ b/client.c
@@ -2,15 +2,15 @@
 // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 #include "include/client.h"
 
+void setSockAddress(int argc, char *argv[], struct sockaddr_in * address);
+
 int main(int argc, char *argv[]) {
-    int fd = 0;
+    int fd;
     if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
         printSystemError("User: socket()");
    
     struct sockaddr_in serv_addr;
-    serv_addr.sin_family = AF_INET;
-    serv_addr.sin_port = htons(PORT);
-    serv_addr.sin_addr.s_addr = inet_addr(ADDRESS);
+    setSockAddress(argc, argv, &serv_addr);
 
     if (connect(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
         printSystemError("User: connect()");
@@ -30,3 +30,30 @@ int main(int argc, char *argv[]) {
 
     return EXIT_SUCCESS;
 }
+
+void setSockAddress(int argc, char *argv[], struct sockaddr_in * address) {
+    address->sin_family = AF_INET;
+
+    if (argc == 1) {
+        address->sin_addr.s_addr = inet_addr(ADDRESS);
+        address->sin_port = htons(PORT);
+    }
+
+    int opt;
+    while ((opt = getopt(argc, argv, "a:p:")) != -1) {
+        switch (opt) {
+        case 'a':
+            if (optarg[0] == '-')
+                printError("Usage: server [-a address] [-p port]\n");
+            address->sin_addr.s_addr = inet_addr(optarg);
+            break;
+        case 'p':
+            if (optarg[0] == '-')
+                printError("Usage: server [-a address] [-p port]\n");
+            address->sin_port = htons(atoi(optarg));
+            break;
+        default:
+            printError("Usage: server [-a address] [-p port]\n");
+        }
+    }
+}
\ No newline at end of file
diff --git a/errors.c b/errors.c
index dd8ed0a..cb59560 100644
--- a/errors.c
+++ b/errors.c
@@ -3,8 +3,7 @@
 #include "include/errors.h"
 
 void printError(char * string) {
-    fprintf(stderr, "%s", string);
-    fprintf(stderr, "\n");
+    fprintf(stderr, "%s\n", string);
     exit(EXIT_FAILURE);
 }
 
diff --git a/include/challenges.h b/include/challenges.h
index a42626a..ae657b1 100644
--- a/include/challenges.h
+++ b/include/challenges.h
@@ -7,11 +7,15 @@
 #include <unistd.h>
 #include <string.h>
 #include <stdlib.h>
+#include <math.h>
 #include "errors.h"
+#include "random.h"
 
 #define MAX_LEN 100
+#define MAX_NORMALS 1000
 #define UPPER 2
 #define LOWER 1
+#define M_PI 3.14159265358979323846
 
 typedef struct challenge_t {
     char * message;
@@ -26,5 +30,6 @@ void filterChallenge();
 void questionChallenge();
 void quineChallenge();
 void gdbChallenge();
+void normalChallenge();
 
 #endif
\ No newline at end of file
diff --git a/include/challengesLib.h b/include/challengesLib.h
new file mode 100644
index 0000000..01c942d
--- /dev/null
+++ b/include/challengesLib.h
@@ -0,0 +1,51 @@
+#ifndef CHALLENGESLIB_H 
+#define CHALLENGESLIB_H 
+
+#include "challenges.h"
+
+#define MAX_CHALLENGES 12
+
+challenge_t challenges[] = {
+    {"Bienvenidos al TP3 y felicitaciones, ya resolvieron el primer acertijo.\n\n\
+En este TP deberán finalizar el juego que ya comenzaron resolviendo los desafíos \
+de cada nivel.\nAdemás tendrán que investigar otras preguntas para responder \
+durante la defensa.\nEl desafío final consiste en crear un programa que se \
+comporte igual que yo, es decir, que provea los mismos desafíos y que sea necesario \
+hacer lo mismo para resolverlos. No basta con esperar la respuesta.\nAdemás, \
+deberán implementar otro programa para comunicarse conmigo.\n\nDeberán estar atentos \
+a los easter eggs.\n\nPara verificar que sus respuestas tienen el formato correcto \
+respondan a este desafío con la palabra 'entendido\\n'", "¿Cómo descubrieron el protocolo, \
+la dirección y el puerto para conectarse?", "entendido\n", NULL},
+    {"The Wire S1E5\n5295 888 6288", "¿Qué diferencias hay entre TCP y UDP y en qué casos \
+conviene usar cada uno?", "itba\n", NULL},
+    {"https://ibb.co/tc0Hb6w", "¿El puerto que usaron para conectarse al server es el mismo \
+que usan para mandar las respuestas? ¿Por qué?", "M4GFKZ289aku\n", NULL},
+    {"EBADF...", "¿Qué útil abstracción es utilizada para comunicarse con sockets? ¿se puede \
+utilizar read(2) y write(2) para operar?", "fk3wfLCm3QvS\n", writeChallenge},
+    {"respuesta = strings:269", "¿Cómo garantiza TCP que los paquetes llegan en orden y no se \
+pierden?", "too_easy\n", NULL},
+    {".data .bss .comment ? .shstrtab .symtab .strtab", "Un servidor suele crear un nuevo proceso \
+o thread para atender las conexiones entrantes. ¿Qué conviene más?", ".RUN_ME\n", NULL},
+    {"Filter error", "¿Cómo se puede implementar un servidor que atienda muchas conexiones sin usar \
+procesos ni threads?", "K5n2UFfpFMUN\n", filterChallenge},
+    {"¿?", "¿Qué aplicaciones se pueden utilizar para ver el tráfico por la red?", 
+    "BUmyYq5XxXGt\n", questionChallenge},
+    {"Latexme\n\nSi\n \\mathrm{d}y = u^v{\\cdot}(v'{\\cdot}\\ln{(u)}+v{\\cdot}\\frac{u'}{u})\n\
+entonces\ny = ", "sockets es un mecanismo de IPC. ¿Qué es más eficiente entre sockets y pipes?",
+    "u^v\n", NULL},
+    {"quine", "¿Cuáles son las características del protocolo SCTP?", "chin_chu_lan_cha\n", quineChallenge},
+    {"b gdbme y encontrá el valor mágico", "¿Qué es un RFC?", "gdb_rules\n", gdbChallenge},
+    {"Me conoces", "¿Fue divertido?", "normal\n", normalChallenge}
+};
+
+char too_easy = 'a';
+char * easter_egg1 = " _______________________\n\
+< ESTO ES UN EASTER_EGG >\n\
+ -----------------------\n\
+        \\   ^__^\n\
+         \\  (oo)\\_______\n\n\
+            (__)\\       )\\/\\\n\
+                ||----w |\n\
+                ||     ||\n";
+
+#endif
\ No newline at end of file
diff --git a/include/client.h b/include/client.h
index 277e179..f123fb2 100644
--- a/include/client.h
+++ b/include/client.h
@@ -1,6 +1,8 @@
 #ifndef CLIENT_H
 #define CLIENT_H
 
+#define _POSIX_C_SOURCE 200809L
+
 #include <stdio.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
diff --git a/include/random.h b/include/random.h
new file mode 100644
index 0000000..dc80db5
--- /dev/null
+++ b/include/random.h
@@ -0,0 +1,8 @@
+#ifndef MATH_H
+#define MATH_H
+
+#include <stdint.h>
+
+double getUniform(uint32_t max);
+
+#endif
\ No newline at end of file
diff --git a/include/server.h b/include/server.h
index 420629c..66839f7 100644
--- a/include/server.h
+++ b/include/server.h
@@ -13,46 +13,14 @@
 #include <netinet/in.h>
 #include "errors.h"
 #include "challenges.h"
+#include "challengesLib.h"
 
 #define MAX_LEN 100
 #define PORT 8080
 #define ADDRESS "127.0.0.1"
 #define MAX_PEND_REQ 3
-#define MAX_CHALLENGES 11
 #define TIME_SLEEP 2
 
-challenge_t challenges[] = {
-    {"Bienvenidos al TP3 y felicitaciones, ya resolvieron el primer acertijo.\n\n\
-En este TP deberán finalizar el juego que ya comenzaron resolviendo los desafíos \
-de cada nivel.\n\nAdemás tendrán que investigar otras preguntas para responder \
-durante la defensa.\n\nEl desafío final consiste en crear un programa que se \
-comporte igual que yo, es decir, que provea los mismos desafíos y que sea necesario \
-hacer lo mismo para resolverlos. No basta con esperar la respuesta.\n\nAdemás, \
-deberán implementar otro programa para comunicarse conmigo.\n\nDeberán estar atentos \
-a los easter eggs.\n\nPara verificar que sus respuestas tienen el formato correcto \
-respondan a este desafío con la palabra 'entendido\\n'", "¿Cómo descubrieron el protocolo, \
-la dirección y el puerto para conectarse?", "entendido\n", NULL},
-    {"The Wire S1E5\n\n5295 888 6288", "¿Qué diferencias hay entre TCP y UDP y en qué casos \
-conviene usar cada uno?", "itba\n", NULL},
-    {"https://ibb.co/tc0Hb6w", "¿El puerto que usaron para conectarse al server es el mismo \
-que usan para mandar las respuestas? ¿Por qué?", "M4GFKZ289aku\n", NULL},
-    {"EBADF...", "¿Qué útil abstracción es utilizada para comunicarse con sockets? ¿se puede \
-utilizar read(2) y write(2) para operar?", "fk3wfLCm3QvS\n", writeChallenge},
-    {"respuesta = strings:269", "¿Cómo garantiza TCP que los paquetes llegan en orden y no se \
-pierden?", "too_easy\n", NULL},
-    {".data .bss .comment ? .shstrtab .symtab .strtab", "Un servidor suele crear un nuevo proceso \
-o thread para atender las conexiones entrantes. ¿Qué conviene más?", ".RUN_ME\n", NULL},
-    {"Filter error", "¿Cómo se puede implementar un servidor que atienda muchas conexiones sin usar \
-procesos ni threads?", "K5n2UFfpFMUN\n", filterChallenge},
-    {"¿?", "¿Qué aplicaciones se pueden utilizar para ver el tráfico por la red?", 
-    "BUmyYq5XxXGt\n", questionChallenge},
-    {"Latexme\n\nSi\n \\mathrm{d}y = u^v{\\cdot}(v'{\\cdot}\\ln{(u)}+v{\\cdot}\\frac{u'}{u})\n\
-entonces\ny =", "sockets es un mecanismo de IPC. ¿Qué es más eficiente entre sockets y pipes?",
-    "u^v\n", NULL},
-    {"quine", "¿Cuáles son las características del protocolo SCTP?", "chin_chu_lan_cha\n", quineChallenge},
-    {"b gdbme y encontrá el valor mágico", "¿Qué es un RFC?", "gdb_rules\n", gdbChallenge},
-};
-
 void startChallenge();
 
 #endif
\ No newline at end of file
diff --git a/quine.c b/quine.c
index 9927c54..c86c7ce 100644
--- a/quine.c
+++ b/quine.c
@@ -1,3 +1 @@
-// This is a personal academic project. Dear PVS-Studio, please check it.
-// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 main() { char *s="main() { char *s=%c%s%c; printf(s,34,s,34,10); }%c"; printf(s,34,s,34,10); }
diff --git a/random.c b/random.c
new file mode 100644
index 0000000..db49d70
--- /dev/null
+++ b/random.c
@@ -0,0 +1,17 @@
+// This is a personal academic project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+#include "include/random.h"
+
+static uint32_t m_z = 362436069;
+static uint32_t m_w = 521288629;
+
+uint32_t getUint() {
+  m_z = 36969 * (m_z & 65535) + (m_z >> 16);
+  m_w = 18000 * (m_w & 65535) + (m_w >> 16);
+  return (m_z << 16) + m_w;
+}
+
+double getUniform(uint32_t max) {
+  uint32_t u = getUint();
+  return (u + 1.0) * 2.328306435454494e-10 * max;
+}
\ No newline at end of file
diff --git a/server.c b/server.c
index e9b97fb..44b7c54 100644
--- a/server.c
+++ b/server.c
@@ -2,6 +2,8 @@
 // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 #include "include/server.h"
 
+void setSockAddress(int argc, char *argv[], struct sockaddr_in * address);
+
 int main(int argc, char *argv[]) {
     int fd;
     if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
@@ -12,10 +14,7 @@ int main(int argc, char *argv[]) {
         printSystemError("Server: setsockopt()");
 
     struct sockaddr_in address;
-    address.sin_family = AF_INET;
-    // address.sin_addr.s_addr = INADDR_ANY;
-    address.sin_addr.s_addr = inet_addr(ADDRESS);
-    address.sin_port = htons(PORT);
+    setSockAddress(argc, argv, &address);
        
     if (bind(fd, (struct sockaddr *) &address, sizeof(address)) < 0) 
         printSystemError("Server: bind()");
@@ -27,6 +26,9 @@ int main(int argc, char *argv[]) {
     if ((fdAux = accept(fd, (struct sockaddr *) &address, (socklen_t *) &addrlen)) < 0)
         printSystemError("Server: accept()");
 
+    if (setvbuf(stdout, NULL, _IONBF, 0) != 0)
+        printSystemError("Server: setvbuf()");
+
     startChallenge(fdAux);
 
     close(fdAux);
@@ -43,7 +45,6 @@ void startChallenge(int fd) {
     int challengeCount = 0;
     char * output = NULL;
 
-    srand(time(NULL));
     while (challengeCount < MAX_CHALLENGES) {
         challenge_t currentChallenge = challenges[challengeCount];
         printf("\033[1;1H\033[2J");
@@ -57,7 +58,38 @@ void startChallenge(int fd) {
         free(output);
     }
 
-    fclose(stream);
+    printf("\033[1;1H\033[2J");
+    printf("Felicitaciones, finalizaron el juego. Ahora deberán implementar el servidor que se comporte como el servidor provisto\n");
+
+    if (stream != NULL)
+        fclose(stream);
 
     return;
+}
+
+void setSockAddress(int argc, char *argv[], struct sockaddr_in * address) {
+    address->sin_family = AF_INET;
+
+    if (argc == 1) {
+        address->sin_addr.s_addr = inet_addr(ADDRESS);
+        address->sin_port = htons(PORT);
+    }
+
+    int opt;
+    while ((opt = getopt(argc, argv, "a:p:")) != -1) {
+        switch (opt) {
+        case 'a':
+            if (optarg[0] == '-')
+                printError("Usage: server [-a address] [-p port]\n");
+            address->sin_addr.s_addr = inet_addr(optarg);
+            break;
+        case 'p':
+            if (optarg[0] == '-')
+                printError("Usage: server [-a address] [-p port]\n");
+            address->sin_port = htons(atoi(optarg));
+            break;
+        default:
+            printError("Usage: server [-a address] [-p port]\n");
+        }
+    }
 }
\ No newline at end of file