From c76f62f4247515b6f756352b493bb492b114833e Mon Sep 17 00:00:00 2001
From: Ada <ada@gnous.eu>
Date: Sat, 2 Mar 2024 23:55:03 +0100
Subject: [PATCH] Public

---
 .clang-format          |  66 ++++++++++++++++
 .clang-tidy            | 148 +++++++++++++++++++++++++++++++++++
 .gitignore             |   4 +
 Makefile               |  23 ++++++
 README.md              |  37 +++++++++
 client/calculateHash.c |  32 ++++++++
 client/calculateHash.h |   9 +++
 client/config.c        | 148 +++++++++++++++++++++++++++++++++++
 client/config.h        |  22 ++++++
 client/crack.c         | 174 +++++++++++++++++++++++++++++++++++++++++
 client/crack.h         |  36 +++++++++
 client/main.c          | 136 ++++++++++++++++++++++++++++++++
 client/main.h          |   6 ++
 default.h              |   3 +
 server/config.c        |  76 ++++++++++++++++++
 server/config.h        |  20 +++++
 server/main.c          | 102 ++++++++++++++++++++++++
 server/main.h          |   6 ++
 shared.c               |  35 +++++++++
 shared.h               |  12 +++
 20 files changed, 1095 insertions(+)
 create mode 100644 .clang-format
 create mode 100644 .clang-tidy
 create mode 100644 .gitignore
 create mode 100644 Makefile
 create mode 100644 README.md
 create mode 100644 client/calculateHash.c
 create mode 100644 client/calculateHash.h
 create mode 100644 client/config.c
 create mode 100644 client/config.h
 create mode 100644 client/crack.c
 create mode 100644 client/crack.h
 create mode 100644 client/main.c
 create mode 100644 client/main.h
 create mode 100644 default.h
 create mode 100644 server/config.c
 create mode 100644 server/config.h
 create mode 100644 server/main.c
 create mode 100644 server/main.h
 create mode 100644 shared.c
 create mode 100644 shared.h

diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..f170fa6
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,66 @@
+# Generated from CLion C/C++ Code Style settings
+BasedOnStyle: LLVM
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: None
+AlignOperands: Align
+AllowAllArgumentsOnNextLine: false
+AllowAllConstructorInitializersOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: Always
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: Always
+AllowShortLambdasOnASingleLine: All
+AllowShortLoopsOnASingleLine: true
+AlwaysBreakAfterReturnType: None
+AlwaysBreakTemplateDeclarations: Yes
+BreakBeforeBraces: Custom
+BraceWrapping:
+  AfterCaseLabel: false
+  AfterClass: false
+  AfterControlStatement: Never
+  AfterEnum: false
+  AfterFunction: false
+  AfterNamespace: false
+  AfterUnion: false
+  BeforeCatch: false
+  BeforeElse: false
+  IndentBraces: false
+  SplitEmptyFunction: false
+  SplitEmptyRecord: true
+BreakBeforeBinaryOperators: None
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeColon
+BreakInheritanceList: BeforeColon
+ColumnLimit: 0
+CompactNamespaces: false
+ContinuationIndentWidth: 8
+IndentCaseLabels: true
+IndentPPDirectives: None
+IndentWidth: 4
+KeepEmptyLinesAtTheStartOfBlocks: true
+MaxEmptyLinesToKeep: 2
+NamespaceIndentation: All
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PointerAlignment: Right
+ReflowComments: false
+SpaceAfterCStyleCast: true
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: false
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 0
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInContainerLiterals: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+TabWidth: 4
+UseTab: Never
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644
index 0000000..532ad47
--- /dev/null
+++ b/.clang-tidy
@@ -0,0 +1,148 @@
+# Generated from CLion Inspection settings
+---
+Checks: '-*,
+bugprone-argument-comment,
+bugprone-assert-side-effect,
+bugprone-bad-signal-to-kill-thread,
+bugprone-branch-clone,
+bugprone-copy-constructor-init,
+bugprone-dangling-handle,
+bugprone-dynamic-static-initializers,
+bugprone-fold-init-type,
+bugprone-forward-declaration-namespace,
+bugprone-forwarding-reference-overload,
+bugprone-inaccurate-erase,
+bugprone-incorrect-roundings,
+bugprone-integer-division,
+bugprone-lambda-function-name,
+bugprone-macro-parentheses,
+bugprone-macro-repeated-side-effects,
+bugprone-misplaced-operator-in-strlen-in-alloc,
+bugprone-misplaced-pointer-arithmetic-in-alloc,
+bugprone-misplaced-widening-cast,
+bugprone-move-forwarding-reference,
+bugprone-multiple-statement-macro,
+bugprone-no-escape,
+bugprone-not-null-terminated-result,
+bugprone-parent-virtual-call,
+bugprone-posix-return,
+bugprone-reserved-identifier,
+bugprone-sizeof-container,
+bugprone-sizeof-expression,
+bugprone-spuriously-wake-up-functions,
+bugprone-string-constructor,
+bugprone-string-integer-assignment,
+bugprone-string-literal-with-embedded-nul,
+bugprone-suspicious-enum-usage,
+bugprone-suspicious-memset-usage,
+bugprone-suspicious-missing-comma,
+bugprone-suspicious-semicolon,
+bugprone-suspicious-string-compare,
+bugprone-suspicious-memory-comparison,
+bugprone-suspicious-realloc-usage,
+bugprone-swapped-arguments,
+bugprone-terminating-continue,
+bugprone-throw-keyword-missing,
+bugprone-too-small-loop-variable,
+bugprone-undefined-memory-manipulation,
+bugprone-undelegated-constructor,
+bugprone-unhandled-self-assignment,
+bugprone-unused-raii,
+bugprone-unused-return-value,
+bugprone-use-after-move,
+bugprone-virtual-near-miss,
+cert-dcl21-cpp,
+cert-dcl58-cpp,
+cert-err34-c,
+cert-err52-cpp,
+cert-err60-cpp,
+cert-flp30-c,
+cert-msc50-cpp,
+cert-msc51-cpp,
+cert-str34-c,
+cppcoreguidelines-interfaces-global-init,
+cppcoreguidelines-narrowing-conversions,
+cppcoreguidelines-pro-type-member-init,
+cppcoreguidelines-pro-type-static-cast-downcast,
+cppcoreguidelines-slicing,
+google-default-arguments,
+google-explicit-constructor,
+google-runtime-operator,
+hicpp-exception-baseclass,
+hicpp-multiway-paths-covered,
+misc-misplaced-const,
+misc-new-delete-overloads,
+misc-non-copyable-objects,
+misc-throw-by-value-catch-by-reference,
+misc-unconventional-assign-operator,
+misc-uniqueptr-reset-release,
+modernize-avoid-bind,
+modernize-concat-nested-namespaces,
+modernize-deprecated-headers,
+modernize-deprecated-ios-base-aliases,
+modernize-loop-convert,
+modernize-make-shared,
+modernize-make-unique,
+modernize-pass-by-value,
+modernize-raw-string-literal,
+modernize-redundant-void-arg,
+modernize-replace-auto-ptr,
+modernize-replace-disallow-copy-and-assign-macro,
+modernize-replace-random-shuffle,
+modernize-return-braced-init-list,
+modernize-shrink-to-fit,
+modernize-unary-static-assert,
+modernize-use-auto,
+modernize-use-bool-literals,
+modernize-use-emplace,
+modernize-use-equals-default,
+modernize-use-equals-delete,
+modernize-use-nodiscard,
+modernize-use-noexcept,
+modernize-use-nullptr,
+modernize-use-override,
+modernize-use-transparent-functors,
+modernize-use-uncaught-exceptions,
+mpi-buffer-deref,
+mpi-type-mismatch,
+openmp-use-default-none,
+performance-faster-string-find,
+performance-for-range-copy,
+performance-implicit-conversion-in-loop,
+performance-inefficient-algorithm,
+performance-inefficient-string-concatenation,
+performance-inefficient-vector-operation,
+performance-move-const-arg,
+performance-move-constructor-init,
+performance-no-automatic-move,
+performance-noexcept-move-constructor,
+performance-trivially-destructible,
+performance-type-promotion-in-math-fn,
+performance-unnecessary-copy-initialization,
+performance-unnecessary-value-param,
+portability-simd-intrinsics,
+readability-avoid-const-params-in-decls,
+readability-const-return-type,
+readability-container-size-empty,
+readability-convert-member-functions-to-static,
+readability-delete-null-pointer,
+readability-deleted-default,
+readability-inconsistent-declaration-parameter-name,
+readability-make-member-function-const,
+readability-misleading-indentation,
+readability-misplaced-array-index,
+readability-non-const-parameter,
+readability-redundant-control-flow,
+readability-redundant-declaration,
+readability-redundant-function-ptr-dereference,
+readability-redundant-smartptr-get,
+readability-redundant-string-cstr,
+readability-redundant-string-init,
+readability-simplify-subscript-expr,
+readability-static-accessed-through-instance,
+readability-static-definition-in-anonymous-namespace,
+readability-string-compare,
+readability-uniqueptr-delete-release,
+readability-use-anyofallof,
+clang-analyzer-*,
+google-*'
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e011c37
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+a.out
+.idea/
+build/
+log
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..fc3f1f3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,23 @@
+CC = clang
+CFLAGS = -Wall -Wextra  -Wconversion -Wdeprecated -Wpedantic -Wshadow -Wuninitialized -Wunused -Wcast-qual -Wfloat-equal -Wformat=2 -Winit-self -Wstrict-prototypes -Wswitch-default -Wswitch-enum -Wundef -Wunused-macros -Wwrite-strings -Wmissing-prototypes --std=gnu17
+
+
+all: clean build
+
+dev: clean build-dbg
+
+build:
+	mkdir build/
+	$(CC) $(CFLAGS) client/main.c client/config.c client/crack.c client/calculateHash.c shared.c -o build/client -lcrypto -lbsd
+	$(CC) $(CFLAGS) server/main.c server/config.c shared.c -o build/server -lbsd
+
+build-dbg:
+	mkdir build/
+	$(CC) $(CFLAGS) -g client/main.c client/config.c client/log.c client/crack.c client/calculateHash.c shared.c -o build/client -lcrypto -lbsd
+	$(CC) $(CFLAGS) -g server/main.c server/config.c server/log.c shared.c -o build/server -lcrypto -lbsd
+
+clean:
+	rm -rf build/
+
+lint:
+	find . -type f -name '*.c' -exec clang-tidy {} \;
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3e991f7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+# Hash breaker
+
+A school project (code is not clean, I know) for break md5 with a client server program. Support wordlist or basic bruteforce based on array of char.
+
+## Configuration :
+
+### Client :
+
+- -a: Server IP
+- -p: Server port
+- -l: Maximum password length
+- -e: Ascii generation end (default 127)
+- -b: Ascii generation begin (default 32)
+- -f: Output error to file
+- -h: Help
+
+### Serveur :
+
+- -i: Listen IP
+- -p: Listen port
+- -s: Hash to break
+- -f: Output log to file
+- -h: Help
+
+# Build
+## Install Dependencies
+### Debian :
+`sudo apt install build-essential libbsd-dev libssl-dev clang`
+
+## Compile
+
+- `make build` output binary in `build/{server,client}`
+- `make build-dbg` output binary in `build/{server,client}` with debug symbol
+- `make lint` lint with `clang-tidy`
+- `make clean` delete `build/`
+- `make` ou `make all` clean+lint+build 
+- `make dev` clean+build-dbg
diff --git a/client/calculateHash.c b/client/calculateHash.c
new file mode 100644
index 0000000..47fa897
--- /dev/null
+++ b/client/calculateHash.c
@@ -0,0 +1,32 @@
+#include <openssl/evp.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../default.h"
+#include "../shared.h"
+
+#include "calculateHash.h"
+
+char *generateMD5(char *string_p) {
+    unsigned char mdValue[EVP_MAX_MD_SIZE];
+    unsigned int mdLength;
+    size_t i;
+    char *formattedHash_p = (char *) calloc(MD5_STR_SIZE, sizeof(char));
+    if (formattedHash_p == NULL) {
+        showError(NULL);
+        return NULL;
+    }
+
+    EVP_MD_CTX *MDctx_p = EVP_MD_CTX_new();
+
+    EVP_DigestInit_ex(MDctx_p, EVP_md5(), NULL);
+    EVP_DigestUpdate(MDctx_p, string_p, strlen(string_p));
+    EVP_DigestFinal_ex(MDctx_p, mdValue, &mdLength);
+
+    for (i = 0; i < mdLength; i++) {
+        snprintf(&formattedHash_p[i * 2], 3, "%02x", mdValue[i]); // Transform int to correct format
+    }
+
+    EVP_MD_CTX_free(MDctx_p);
+    return formattedHash_p;
+}
diff --git a/client/calculateHash.h b/client/calculateHash.h
new file mode 100644
index 0000000..e743cab
--- /dev/null
+++ b/client/calculateHash.h
@@ -0,0 +1,9 @@
+#ifndef CALCULATEHASH_H
+#define CALCULATEHASH_H
+
+#include "config.h"
+
+char *generateMD5(
+        char *string_p); // Generate and store md5 hash into array of char pointer. Take string_p for source string, formattedHash for formatted result string.
+
+#endif
diff --git a/client/config.c b/client/config.c
new file mode 100644
index 0000000..98013b0
--- /dev/null
+++ b/client/config.c
@@ -0,0 +1,148 @@
+#include <stdio.h>
+#include <bsd/string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sysexits.h>
+#include <linux/limits.h>
+
+#include "../default.h"
+#include "../shared.h"
+
+#include "main.h"
+
+#include "config.h"
+
+int setConfig(int argc, char *argv[], config_t *currentConfig_p) {
+    int numberOptions;
+
+    while ((numberOptions = getopt(argc, argv, "a:p:l:b:e:f:w:h")) != -1) {
+        // a: Server address
+        // p: Server port
+        // l: Max password length (default 8)
+        // b: Ascii generation begin (default 32)
+        // e: Ascii generation end (default 127)
+        // f: Log file
+        // w: Wordlist
+        // h: Help
+        currentConfig_p->AsciiBegin = 32;
+        currentConfig_p->AsciiEnd = 127;
+        currentConfig_p->maxLength = 8;
+        currentConfig_p->logToFile = false;
+        currentConfig_p->wordlist = false;
+        switch (numberOptions) {
+            case 'a':
+                if (inet_addr(optarg) == INADDR_NONE) {
+                    fprintf(stderr, "Adresse IP du serveur invalide.\n");
+                    return EX_CONFIG;
+                }
+                currentConfig_p->serverIP = inet_addr(optarg);
+                break;
+            case 'f':
+                if (strncmp(optarg, "", PATH_MAX) == 0) { // Max size based on POSIX spec
+                    break;
+                }
+                currentConfig_p->logToFile = true;
+                strlcpy(currentConfig_p->logFile, optarg, PATH_MAX);
+                break;
+            case 'w':
+                if (strncmp(optarg, "", PATH_MAX) == 0) { // Max size based on POSIX spec
+                    break;
+                }
+                currentConfig_p->wordlist = true;
+                strlcpy(currentConfig_p->wordlistPath, optarg, PATH_MAX);
+                break;
+            case 'p':
+                errno = 0;
+                if (strtol(optarg, NULL, 10) > MIN_PORT && strtol(optarg, NULL, 10) <= MAX_PORT) {
+                    currentConfig_p->serverPort = (unsigned short) strtol(optarg, NULL, 10);
+                } else {
+                    fprintf(stderr, "Invalid port.\n");
+                    printHelp();
+                    return EX_CONFIG;
+                }
+                if (errno == ERANGE) {
+                    showError(NULL);
+                    printHelp();
+                    return EX_CONFIG;
+                }
+                break;
+            case 'l':
+                errno = 0;
+                if (strtol(optarg, NULL, 10) < 0) {
+                    fprintf(stderr, "length is invalid, must be >0.\n");
+                    printHelp();
+                    return EX_CONFIG;
+                }
+
+                currentConfig_p->maxLength = (unsigned short) strtol(optarg, NULL, 10);
+                if (errno == ERANGE) {
+                    showError(NULL);
+                    printHelp();
+                    return EX_CONFIG;
+                }
+                break;
+            case 'b':
+                errno = 0;
+                if (strtol(optarg, NULL, 10) < 0) {
+                    fprintf(stderr, "Invalid settings, must be >0.\n");
+                    printHelp();
+                    return EX_CONFIG;
+                }
+                if (strtol(optarg, NULL, 10) > 127) {
+                    fprintf(stderr, "Invalid settings, must be =<127.\n");
+                    printHelp();
+                    return EX_CONFIG;
+                }
+
+                currentConfig_p->AsciiBegin = (unsigned short) strtol(optarg, NULL, 10);
+                if (errno == ERANGE) {
+                    showError(NULL);
+                    printHelp();
+                    return EX_CONFIG;
+                }
+                break;
+            case 'e':
+                errno = 0;
+                if (strtol(optarg, NULL, 10) < 0) {
+                    fprintf(stderr, "Invalid settings, must be >0.\n");
+                    printHelp();
+                    return EX_CONFIG;
+                }
+                if (strtol(optarg, NULL, 10) > 127) {
+                    fprintf(stderr, "Invalid settings, must be =<127.\n");
+                    printHelp();
+                    return EX_CONFIG;
+                }
+
+                currentConfig_p->AsciiEnd = (unsigned short) strtol(optarg, NULL, 10);
+                if (errno == ERANGE) {
+                    showError(NULL);
+                    printHelp();
+                    return EX_CONFIG;
+                }
+                break;
+            case 'h':
+                printHelp();
+                exit(EXIT_SUCCESS);
+            default:
+                printHelp();
+                return EX_CONFIG;
+        }
+    }
+
+    // Verify mandatory options.
+    if (currentConfig_p->maxLength == 0) {
+        printHelp();
+        return EX_CONFIG;
+    }
+
+    if (currentConfig_p->serverPort == 0) {
+        printHelp();
+        return EX_CONFIG;
+    }
+    return EXIT_SUCCESS;
+}
diff --git a/client/config.h b/client/config.h
new file mode 100644
index 0000000..4a4ad35
--- /dev/null
+++ b/client/config.h
@@ -0,0 +1,22 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <linux/limits.h>
+
+typedef struct config_s { // store all config.
+    uint32_t serverIP;
+    unsigned short serverPort;
+    unsigned short maxLength;
+    unsigned short AsciiBegin; // Begin of ascii table for array generation
+    unsigned short AsciiEnd; // Begin of ascii table for array generation
+    bool logToFile; // bool for check if log to file is enabled
+    char logFile[PATH_MAX]; // Path for logfile
+    bool wordlist; // bool for check if wordlist mode is enabled
+    char wordlistPath[PATH_MAX]; // path to wordlist mode.
+} config_t;
+
+int setConfig(int argc, char *argv[], config_t *currentConfig_p); // set config from command line argument.
+
+#endif
diff --git a/client/crack.c b/client/crack.c
new file mode 100644
index 0000000..b461599
--- /dev/null
+++ b/client/crack.c
@@ -0,0 +1,174 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "../default.h"
+#include "../shared.h"
+
+#include "calculateHash.h"
+
+#include "crack.h"
+
+void freeCharList_s(charList_t *s) {
+    safeFree((void **) &s->charList);
+    safeFree((void **) &s);
+}
+
+bruteforceResult_t *initBruteforceResult_s(void) {
+    bruteforceResult_t *s = (bruteforceResult_t *) calloc(1, sizeof(bruteforceResult_t));
+    if (s == NULL) {
+        showError(NULL);
+        return NULLPTR;
+    }
+    s->resultSize = (size_t) NULL;
+    s->result = (char *) NULL;
+    s->found = false;
+    return s;
+}
+
+void freeBruteforceResult_s(bruteforceResult_t *s) {
+    safeFree((void **) &s->result);
+    safeFree((void **) &s);
+}
+
+int bruteforce(char *prefix_p, unsigned short length, char *toCrackHash_p, bruteforceResult_t *bruteforceResult_p,
+               charList_t *charList_p) {
+    char *hash_p = NULLPTR;
+    if (length == 0) {
+        hash_p = generateMD5(prefix_p);
+        if (hash_p == NULL) {
+            showError(NULL);
+            return 1;
+        }
+
+        if (strncmp(toCrackHash_p, hash_p, MD5_STR_SIZE * sizeof(char)) == 0) {
+            safeFree((void **) &hash_p);
+            strlcpy(bruteforceResult_p->result, prefix_p, bruteforceResult_p->resultSize);
+            return 0;
+        }
+
+        safeFree((void **) &hash_p);
+        return 1;
+    }
+
+    char c;
+
+    for (unsigned int i = 0; i < length; i++) {
+        for (unsigned int j = 0; j < charList_p->arraySize; j++) {
+            c = charList_p->charList[j];
+            prefix_p[length - 1] = c;
+            if (bruteforce(prefix_p, length - 1, toCrackHash_p, bruteforceResult_p, charList_p) == 0) {
+                safeFree((void **) &hash_p);
+                return 0;
+            }
+        }
+    }
+
+    safeFree((void **) &hash_p);
+    return 1;
+}
+
+void incrementalBruteforce(unsigned short length, char *toCrackHash_p, bruteforceResult_t *bruteforceResult_p,
+                           charList_t *charList_p) {
+    char *prefix_p = (char *) calloc((length + 1), sizeof(char));
+    if (prefix_p == NULL) {
+        showError(NULL);
+        return;
+    }
+
+    for (unsigned short i = 1; i <= length; i++) {
+        bruteforceResult_p->resultSize = (i + 1) * sizeof(char);
+        bruteforceResult_p->result = (char *) calloc(1, bruteforceResult_p->resultSize);
+        if (bruteforceResult_p == NULL) {
+            safeFree((void **) &prefix_p);
+            showError(NULL);
+            return;
+        }
+
+        if (bruteforce(prefix_p, i, toCrackHash_p, bruteforceResult_p, charList_p) == 0) {
+            safeFree((void **) &prefix_p);
+            bruteforceResult_p->found = true;
+            return;
+        }
+        safeFree((void **) &bruteforceResult_p->result);
+        bruteforceResult_p->resultSize = 0;
+    }
+
+    safeFree((void **) &prefix_p);
+}
+
+charList_t *generateAsciiRange(unsigned short begin, unsigned short end) {
+    charList_t *charList_p = (charList_t *) calloc(1, sizeof(charList_t));
+    if (charList_p == NULL) {
+        showError(NULL);
+        return NULL;
+    }
+    charList_p->arraySize = (size_t) end - begin;
+    charList_p->charList = (char *) calloc(charList_p->arraySize + 1, sizeof(char));
+    if (charList_p->charList == NULL) {
+        showError(NULL);
+        return NULLPTR;
+    }
+    for (unsigned short i = begin; i <= end; i++) {
+        charList_p->charList[i - begin] = (char) i;
+    }
+    return charList_p;
+}
+
+void bruteforceWordlist(const char *toCrackHash_p, const char *wordlist_p, bruteforceResult_t *bruteforceResult_p) {
+    errno = 0;
+    if (bruteforceResult_p == NULL) {
+        return;
+    }
+    FILE *f = fopen(wordlist_p, "r");
+
+
+    if (f == NULL) {
+        showError(NULL);
+        return;
+    }
+
+    char *line = NULLPTR;
+    size_t len = 0;
+    ssize_t currentRead;
+    char *hash_p = NULLPTR;
+
+    while ((currentRead = getline(&line, &len, f)) != -1) {
+        line[strcspn(line, "\r\n")] = 0;
+        hash_p = generateMD5(line);
+        if (hash_p == NULL) {
+            showError(NULL);
+            fclose(f);
+            safeFree((void **) &line);
+            return;
+        }
+
+        if (strncmp(toCrackHash_p, hash_p, MD5_STR_SIZE * sizeof(char)) == 0) {
+            bruteforceResult_p->resultSize = (unsigned long) currentRead + 1 * sizeof(char);
+            bruteforceResult_p->result = (char *) calloc(1, bruteforceResult_p->resultSize);
+            if (bruteforceResult_p->result == NULL) {
+                showError(NULL);
+                freeBruteforceResult_s(bruteforceResult_p);
+                safeFree((void **) &line);
+                safeFree((void **) &hash_p);
+                fclose(f);
+                return;
+            }
+            bruteforceResult_p->found = true;
+            strlcpy(bruteforceResult_p->result, line, bruteforceResult_p->resultSize);
+
+            safeFree((void **) &hash_p);
+            safeFree((void **) &line);
+            fclose(f);
+            return;
+        } else {
+            safeFree((void **) &hash_p);
+        }
+    }
+
+    fclose(f);
+    safeFree((void **) &hash_p);
+    safeFree((void **) &line);
+}
diff --git a/client/crack.h b/client/crack.h
new file mode 100644
index 0000000..c33903b
--- /dev/null
+++ b/client/crack.h
@@ -0,0 +1,36 @@
+#ifndef CRACK_H
+#define CRACK_H
+
+#include <stddef.h>
+#include <stdbool.h>
+
+typedef struct CharList_s { // List of char for bruteforce step
+    size_t arraySize;
+    char *charList;
+} charList_t;
+
+typedef struct bruteforceResult_s { // store result of bruteforce
+    bool found;
+    size_t resultSize;
+    char *result;
+} bruteforceResult_t;
+
+int bruteforce(char *prefix, unsigned short length, char *toCrackHash, bruteforceResult_t *bruteforceResult,
+               charList_t *charList); // bruteforce a specific length, with a specific array of char.
+
+void incrementalBruteforce(unsigned short length, char *toCrackHash_p, bruteforceResult_t *bruteforceResult_p,
+                           charList_t *charList_p); // bruteforce from 0 to max length with an array of char. Take (max) length, pointer for hash to crack, bruteForceResult_s structure, charList_s structure.
+
+charList_t *generateAsciiRange(unsigned short begin,
+                               unsigned short end); // Generate array of char with range of ASCII character, take begin of range, end of range. Return pointer to charList_s structure.
+
+void freeCharList_s(charList_t *s); // free charList_s, take pointer to structure
+
+bruteforceResult_t *initBruteforceResult_s(void); // Initialise bruteforceResult_s, return pointer to structure.
+
+void freeBruteforceResult_s(bruteforceResult_t *s); // free bruteforceResult_s, take pointer to structure.
+
+void bruteforceWordlist(const char *toCrackHash_p, const char *wordlist_p,
+                        bruteforceResult_t *bruteforceResult_p); // Bruteforce with wordlist, pointer for hash to crack, a bruteforceResult_s structure and a pointer for currentConfig.
+
+#endif
diff --git a/client/main.c b/client/main.c
new file mode 100644
index 0000000..c90280f
--- /dev/null
+++ b/client/main.c
@@ -0,0 +1,136 @@
+#include <errno.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "../default.h"
+#include "../shared.h"
+
+#include "config.h"
+#include "crack.h"
+
+#include "main.h"
+
+void printHelp(void) {
+    printf("Options are : \n  -a: IPv4 address for server\n  -p: Server port\n  -l: Max length\n  -h: This message\n  -b: Ascii generation begin (default 32)\n  -e: Ascii generation end (default 127)\n -f: Log file (default stderr)\n");
+}
+
+// Manage config (argv + getopt()), contact server and start cracking.
+int main(int argc, char *argv[]) {
+    config_t currentConfig;
+
+    errno = 0;
+    int setConfigReturn = setConfig(argc, argv, &currentConfig);
+    if (setConfigReturn != 0) {
+        return setConfigReturn;
+    }
+
+    if (currentConfig.logToFile == true) {
+        showError((char *) &currentConfig.logFile);
+    }
+
+    int clientSockD = socket(AF_INET, SOCK_STREAM, 0);
+    if (clientSockD == -1) {
+        showError(NULL);
+        return errno;
+    }
+    struct sockaddr_in serverAddress;
+
+    serverAddress.sin_family = AF_INET;
+    serverAddress.sin_port = htons(currentConfig.serverPort);
+    serverAddress.sin_addr.s_addr = currentConfig.serverIP;
+
+    if (connect(clientSockD, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == -1) {
+        showError(NULL);
+        close(clientSockD);
+        return errno;
+    }
+
+    char *hash_p = (char *) calloc(MD5_STR_SIZE, sizeof(char));
+    if (hash_p == NULL) {
+        showError(NULL);
+        close(clientSockD);
+        return errno;
+    }
+
+    if (send(clientSockD, "hash", sizeof("hash"), 0) == -1) {
+        showError(NULL);
+        close(clientSockD);
+        safeFree((void **) &hash_p);
+        return errno;
+    }
+
+    if (recv(clientSockD, hash_p, MD5_STR_SIZE * sizeof(char), 0) == -1) {
+        showError(NULL);
+        close(clientSockD);
+        safeFree((void **) &hash_p);
+        return errno;
+    }
+
+    printf("Hash : %s\n", hash_p);
+
+
+    bruteforceResult_t *bruteforceResult_p = initBruteforceResult_s();
+    if (bruteforceResult_p == NULLPTR) {
+        close(clientSockD);
+        safeFree((void **) &hash_p);
+        return errno;
+    }
+
+    if (currentConfig.wordlist == true) {
+        bruteforceWordlist(hash_p, currentConfig.wordlistPath, bruteforceResult_p);
+    } else {
+        charList_t *charList = generateAsciiRange(currentConfig.AsciiBegin, currentConfig.AsciiEnd);
+        if (charList == NULL) {
+            freeBruteforceResult_s(bruteforceResult_p);
+            close(clientSockD);
+            safeFree((void **) &hash_p);
+            return EXIT_FAILURE;
+        }
+
+        bruteforceResult_p->resultSize = (currentConfig.maxLength + 1) * sizeof(char);
+        bruteforceResult_p->result = NULLPTR;
+
+        incrementalBruteforce(currentConfig.maxLength, hash_p, bruteforceResult_p, charList);
+        freeCharList_s(charList);
+    }
+
+    if (bruteforceResult_p->found == true) {
+        printf("Answer : %s - %s\n", hash_p, bruteforceResult_p->result);
+
+        if (send(clientSockD, "found", sizeof("found"), 0) == -1) {
+            showError(NULL);
+            close(clientSockD);
+            safeFree((void **) &hash_p);
+            freeBruteforceResult_s(bruteforceResult_p);
+            return errno;
+        }
+
+        sleep(1);
+        if (send(clientSockD, bruteforceResult_p->result, bruteforceResult_p->resultSize, 0) == -1) {
+            showError(NULL);
+            close(clientSockD);
+            safeFree((void **) &hash_p);
+            freeBruteforceResult_s(bruteforceResult_p);
+            return errno;
+        }
+
+    } else {
+        if (send(clientSockD, "notfound", sizeof("notfound"), 0) == -1) {
+            showError(NULL);
+            close(clientSockD);
+            safeFree((void **) &hash_p);
+            freeBruteforceResult_s(bruteforceResult_p);
+            return errno;
+        }
+
+        printf("Response not found.\n");
+    }
+
+    close(clientSockD);
+    freeBruteforceResult_s(bruteforceResult_p);
+    safeFree((void **) &hash_p);
+    return EXIT_SUCCESS;
+}
diff --git a/client/main.h b/client/main.h
new file mode 100644
index 0000000..c490ba0
--- /dev/null
+++ b/client/main.h
@@ -0,0 +1,6 @@
+#ifndef MAIN_H
+#define MAIN_H
+
+void printHelp(void); // Print help message
+
+#endif
diff --git a/default.h b/default.h
new file mode 100644
index 0000000..eedc9cc
--- /dev/null
+++ b/default.h
@@ -0,0 +1,3 @@
+#define MIN_PORT 0
+#define MAX_PORT 65535
+#define MD5_STR_SIZE 33 // 32+1
diff --git a/server/config.c b/server/config.c
new file mode 100644
index 0000000..475141e
--- /dev/null
+++ b/server/config.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <bsd/string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sysexits.h>
+
+#include "../default.h"
+#include "../shared.h"
+
+#include "main.h"
+
+#include "config.h"
+
+int setConfig(int argc, char *argv[], config_t *currentConfig_p) {
+    // Set config
+    // - currentConfig_p : Actual config
+    // return : 0 if success, else if error
+    int numberOptions;
+
+    while ((numberOptions = getopt(argc, argv, "a:p:s:f:h")) != -1) {
+        currentConfig_p->logToFile = false;
+        switch (numberOptions) {
+            case 'f':
+                if (strncmp(optarg, "", 4096) == 0) { // Max size based on POSIX spec
+                    break;
+                }
+                currentConfig_p->logToFile = true;
+                strlcpy(currentConfig_p->logFile, optarg, 4096);
+                break;
+            case 'a':
+                currentConfig_p->serverIP = inet_addr(optarg);
+                break;
+            case 'p':
+                errno = 0;
+                if (strtol(optarg, NULL, 10) > MIN_PORT && strtol(optarg, NULL, 10) <= MAX_PORT) {
+                    currentConfig_p->serverPort = (unsigned short) strtol(optarg, NULL, 10);
+                } else {
+                    fprintf(stderr, "Port invalide.\n");
+                    printHelp();
+                    return EX_CONFIG;
+                }
+                if (errno == ERANGE) {
+                    showError(NULL);
+                    printHelp();
+                    return EX_CONFIG;
+                }
+                break;
+            case 's':
+                errno = 0;
+                strlcpy(currentConfig_p->hash, optarg, MD5_STR_SIZE * sizeof(char));
+                if (errno == ERANGE) {
+                    showError(NULL);
+                    printHelp();
+                    return EX_CONFIG;
+                }
+                break;
+            case 'h':
+                printHelp();
+                exit(EXIT_SUCCESS);
+            default:
+                printHelp();
+                return EX_CONFIG;
+        }
+    }
+
+    // Verify mandatory options.
+    if (currentConfig_p->serverPort == 0) {
+        fprintf(stderr, "Port invalide ou manquant.\n");
+        printHelp();
+        return EX_CONFIG;
+    }
+    return 0;
+}
diff --git a/server/config.h b/server/config.h
new file mode 100644
index 0000000..1304a4d
--- /dev/null
+++ b/server/config.h
@@ -0,0 +1,20 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <linux/limits.h>
+
+#include "../default.h"
+
+typedef struct config_s {
+    uint32_t serverIP;
+    char hash[MD5_STR_SIZE];
+    unsigned short serverPort;
+    bool logToFile; // bool for check if log to file is enabled
+    char logFile[PATH_MAX]; // Path for logfile
+} config_t;
+
+int setConfig(int argc, char *argv[], config_t *currentConfig_p);
+
+#endif
diff --git a/server/main.c b/server/main.c
new file mode 100644
index 0000000..5ea30bb
--- /dev/null
+++ b/server/main.c
@@ -0,0 +1,102 @@
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "../default.h"
+#include "../shared.h"
+
+#include "config.h"
+
+#include "main.h"
+
+
+void printHelp(void) {
+    printf("Options are : \n  -a: IPv4 address for socket\n  -p: Port to listen\n  -s: Hash to crack\n  -h: This message\n");
+}
+
+int main(int argc, char *argv[]) {
+
+    config_t currentConfig; // Global config structure
+
+    int setConfigReturn = setConfig(argc, argv, &currentConfig);
+    if (setConfigReturn != 0) {
+        return setConfigReturn;
+    }
+
+    if (currentConfig.logToFile == true) {
+        showError((char *) &currentConfig.logFile);
+    }
+
+    int serverSockD = socket(AF_INET, SOCK_STREAM, 0);
+    int opt = 1;
+    if ((setsockopt(serverSockD, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) == -1) {
+        showError(NULL);
+        close(serverSockD);
+        return errno;
+    }
+    struct sockaddr_in serverAddress;
+
+    serverAddress.sin_family = AF_INET;
+    serverAddress.sin_port = htons(currentConfig.serverPort);
+    serverAddress.sin_addr.s_addr = currentConfig.serverIP;
+
+    if (bind(serverSockD, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == -1) {
+        showError(NULL);
+        close(serverSockD);
+        return errno;
+    }
+
+    if (listen(serverSockD, 1) == -1) {
+        showError(NULL);
+        close(serverSockD);
+        return errno;
+    }
+    int clientSockD = accept(serverSockD, NULL, NULL);
+    if (clientSockD == -1) {
+        showError(NULL);
+        close(serverSockD);
+        return errno;
+    }
+
+    char clientResponseBuffer[8192];
+    for (;;) {
+        ssize_t recvResult;
+        recvResult = recv(clientSockD, clientResponseBuffer, sizeof(clientResponseBuffer) - 1, 0);
+        if (recvResult == -1) {
+            showError(NULL);
+            close(clientSockD);
+            close(serverSockD);
+            return errno;
+        }
+        clientResponseBuffer[recvResult] = 0;
+
+        if (strncmp(clientResponseBuffer, "hash", sizeof(clientResponseBuffer)) == 0) {
+            if (send(clientSockD, (char *) &currentConfig.hash, sizeof(currentConfig.hash) - 1, 0) == -1) {
+                showError(NULL);
+                close(clientSockD);
+                close(serverSockD);
+                return errno;
+            }
+        } else if (strncmp(clientResponseBuffer, "found", sizeof(clientResponseBuffer)) == 0) {
+            recvResult = recv(clientSockD, clientResponseBuffer, sizeof(clientResponseBuffer) - 1, 0);
+            if (recvResult == -1) {
+                showError(NULL);
+                close(serverSockD);
+                close(clientSockD);
+                return errno;
+            }
+            clientResponseBuffer[recvResult] = 0;
+            printf("Response is : %s - %s\n", currentConfig.hash, clientResponseBuffer);
+            break;
+        } else if (strncmp(clientResponseBuffer, "notfound", sizeof(clientResponseBuffer)) == 0) {
+            printf("Response not found.\n");
+            break;
+        }
+    }
+    close(serverSockD);
+    close(clientSockD);
+    return 0;
+}
diff --git a/server/main.h b/server/main.h
new file mode 100644
index 0000000..f1daf43
--- /dev/null
+++ b/server/main.h
@@ -0,0 +1,6 @@
+#ifndef MAIN_H
+#define MAIN_H
+
+void printHelp(void);
+
+#endif
diff --git a/shared.c b/shared.c
new file mode 100644
index 0000000..8f62f7d
--- /dev/null
+++ b/shared.c
@@ -0,0 +1,35 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <linux/limits.h>
+
+#include "shared.h"
+
+void safeFree(void **toFree) {
+    if (*toFree != NULL || *toFree != NULLPTR) {
+        free(*toFree);
+        *toFree = NULLPTR;
+    }
+}
+
+void showError(const char *logFilePath_p) {
+    static char staticLogFilePath[PATH_MAX];
+
+    if (logFilePath_p != NULL) {
+        strlcpy(staticLogFilePath, logFilePath_p, PATH_MAX);
+        return;
+    }
+
+    if (staticLogFilePath[0] != 0) {
+        FILE *f = fopen(staticLogFilePath, "a");
+        if (f == NULL) {
+            fprintf(stderr, "Impossible d'ouvrir le fichier de log. :\n%s\n", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        fprintf(f, "%s\n", strerror(errno));
+        fclose(f);
+        return;
+    }
+    fprintf(stderr, "%s\n", strerror(errno));
+}
diff --git a/shared.h b/shared.h
new file mode 100644
index 0000000..f0255a3
--- /dev/null
+++ b/shared.h
@@ -0,0 +1,12 @@
+#ifndef SHARED_H
+#define SHARED_H
+
+#define NULLPTR ((void*)0) // Point to 0x0, better than just NULL (0)
+
+#include <stdbool.h>
+
+void safeFree(void **toFree);
+
+void showError(const char *logFilePath_p); // Show error, based on errno. Initialise it with logToFile et logFilePath.
+
+#endif