diff --git a/bakanet/premake5.lua b/bakanet/premake5.lua new file mode 100644 index 0000000..fd74a2d --- /dev/null +++ b/bakanet/premake5.lua @@ -0,0 +1,51 @@ +project "bakanet" + location "./bakanet" + kind "StaticLib" + language "C++" + cppdialect "C++17" + systemversion "latest" + + targetdir("%{wks.location}/bin/" .. outputdir .. "/%{prj.name}") + objdir("%{wks.location}/bin-int/" .. outputdir .. "/%{prj.name}") + + includedirs + { + "%{IncludeDirs.bakanet}" + } + + files + { + "%{prj.location}/src/bakanet/**.h", + "%{prj.location}/src/bakanet/**.cpp", + "%{prj.location}/src/bakanet.h", + "%{prj.location}/src/baknetpch.h", + } + + filter "system:windows" + defines + { + "BK_PLAFORM_WINDOWS" + } + + files + { + "%{prj.location}/src/plaform/windows/**.h", + "%{prj.location}/src/plaform/windows/**.cpp", + } + + links + { + "WS2_32.lib" + } + + filter "system:linux" + defines + { + "BK_PLAFORM_LINUX" + } + + files + { + "%{prj.location}/src/plaform/linux/**.h", + "%{prj.location}/src/plaform/linux/**.cpp", + } diff --git a/bakanet/src/bakanet.h b/bakanet/src/bakanet.h new file mode 100644 index 0000000..6498047 --- /dev/null +++ b/bakanet/src/bakanet.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include \ No newline at end of file diff --git a/bakanet/src/bakanet/http/http_parser.cpp b/bakanet/src/bakanet/http/http_parser.cpp new file mode 100644 index 0000000..8554440 --- /dev/null +++ b/bakanet/src/bakanet/http/http_parser.cpp @@ -0,0 +1,42 @@ +#include "http_parser.h" + +HttpRequest http_parser(std::string req) +{ + std::string url = "", method = "", body = ""; + std::unordered_map params; + + auto lines = string_split(req, "\n"); + + auto first_line = string_split(lines->at(0), " "); + method = string_to_upper(first_line->at(0)); + url = first_line->at(1); + body = lines->at(lines->size() - 1); + + lines->erase(lines->begin()); + lines->pop_back(); + + for (auto line : *lines) + { + auto param = string_split(line, ":", 1); + if (param->size() == 2) + { + params.insert({ param->at(0), string_trim(param->at(1))}); + } + } + return HttpRequest { + http_resolve_methode(method), + url, + params, + body + }; +} + + +HttpMethod http_resolve_methode(std::string method) +{ + if (method.compare("GET")) return HttpMethod::GET; + else if (method.compare("POST")) return HttpMethod::POST; + else if (method.compare("PUT")) return HttpMethod::PUT; + else if (method.compare("DELETE")) return HttpMethod::DELETE; + else return HttpMethod::NONE; +} \ No newline at end of file diff --git a/bakanet/src/bakanet/http/http_parser.h b/bakanet/src/bakanet/http/http_parser.h new file mode 100644 index 0000000..d725ebd --- /dev/null +++ b/bakanet/src/bakanet/http/http_parser.h @@ -0,0 +1,26 @@ +#include + +#include + +#include "http_tools.h" + +enum class HttpMethod +{ + NONE = 0, + GET = 1, + POST = 2, + PUT = 3, + DELETE = 4, +}; + +class HttpRequest +{ + public: + HttpMethod method; + std::string url; + std::unordered_map params; + std::string body; +}; + +HttpRequest http_parser(std::string req); +HttpMethod http_resolve_methode(std::string method); \ No newline at end of file diff --git a/bakanet/src/bakanet/http/http_server.cpp b/bakanet/src/bakanet/http/http_server.cpp new file mode 100644 index 0000000..49c1d0e --- /dev/null +++ b/bakanet/src/bakanet/http/http_server.cpp @@ -0,0 +1,48 @@ +#include "http_server.h" + +void http_server() +{ + IpAddress ip("127.0.0.1"); + Socket sock(ip, PORT, IpProtocol::TCP); + bool running = sock.init() && sock.start(5); + char input = 'N'; + + do + { + Connection conn = sock.ack(); + if (conn >= 0) + { + std::string http_request(http_handler(sock, conn)); + + if (http_request == "") continue; + HttpRequest req = http_parser(http_request); + log("Http request"); + log("Method " << (int)req.method) + log("URL " << req.url) + log("Body " << req.body) + } + log("Close?") + input(input); + } while (input != 'y'); + +} + +std::string http_handler(Socket& sock, Connection conn) +{ + Packet req; + bool reading = true; + while(reading) + { + std::vector raw_data; + raw_data = sock.recv(conn, 4); + reading = req.append_data(raw_data); + } + + close(conn); + + int req_size = req.size(); + std::unique_ptr req_test = req.pull(req_size); + + if (req_size) return std::string(req_test.release(), req_size); + return ""; +} \ No newline at end of file diff --git a/bakanet/src/bakanet/http/http_server.h b/bakanet/src/bakanet/http/http_server.h new file mode 100644 index 0000000..f8c6e69 --- /dev/null +++ b/bakanet/src/bakanet/http/http_server.h @@ -0,0 +1,7 @@ +#include + +#include "http_parser.h" + +using namespace Bk::Net; +void http_server(); +std::string http_handler(Socket& sock, Connection conn); \ No newline at end of file diff --git a/bakanet/src/bakanet/http/http_tools.cpp b/bakanet/src/bakanet/http/http_tools.cpp new file mode 100644 index 0000000..d192bf4 --- /dev/null +++ b/bakanet/src/bakanet/http/http_tools.cpp @@ -0,0 +1,34 @@ +#include "http_tools.h" + +std::string string_to_upper(std::string& str) +{ + for (int i = 0; i < str.length(); i++) + { + str[i] = std::toupper(str[i]); + } + return str; +} + +std::unique_ptr> string_split(std::string s, std::string delimiter, int cpt) +{ + std::unique_ptr> splits(new std::vector(0)); + size_t pos = 0; + while (((pos = s.find(delimiter)) != std::string::npos) && cpt-- != 0) { + splits->push_back(s.substr(0, pos)); + s.erase(0, pos + delimiter.length()); + } + splits->push_back(s); + return splits; +} + +std::string string_trim(const std::string& str, const std::string& whitespace) +{ + const auto strBegin = str.find_first_not_of(whitespace); + if (strBegin == std::string::npos) + return ""; // no content + + const auto strEnd = str.find_last_not_of(whitespace); + const auto strRange = strEnd - strBegin + 1; + + return str.substr(strBegin, strRange); +} \ No newline at end of file diff --git a/bakanet/src/bakanet/http/http_tools.h b/bakanet/src/bakanet/http/http_tools.h new file mode 100644 index 0000000..c555b7d --- /dev/null +++ b/bakanet/src/bakanet/http/http_tools.h @@ -0,0 +1,7 @@ +#include + +#include + +std::string string_to_upper(std::string& str); +std::unique_ptr> string_split(std::string s, std::string delimiter, int cpt = -1); +std::string string_trim(const std::string& str, const std::string& whitespace = " "); \ No newline at end of file diff --git a/bakanet/src/bakanet/sock_layer/dns_lookup.h b/bakanet/src/bakanet/sock_layer/dns_lookup.h new file mode 100644 index 0000000..43208ba --- /dev/null +++ b/bakanet/src/bakanet/sock_layer/dns_lookup.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include "ip_version.h" +#include "ip_address.h" + +namespace Bk::Net { + std::vector dns_lookup(const std::string& host_name, IpVersion ipv); +} \ No newline at end of file diff --git a/bakanet/src/bakanet/sock_layer/ip_address.h b/bakanet/src/bakanet/sock_layer/ip_address.h new file mode 100644 index 0000000..42e5ce4 --- /dev/null +++ b/bakanet/src/bakanet/sock_layer/ip_address.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include "ip_version.h" + +namespace Bk::Net { + class IpAddress + { + public: + IpAddress(std::string ip, IpVersion ipv = IpVersion::IPv4); + void* get_data(); + std::string str; + IpVersion version; + }; +} \ No newline at end of file diff --git a/bakanet/src/bakanet/sock_layer/ip_protocol.h b/bakanet/src/bakanet/sock_layer/ip_protocol.h new file mode 100644 index 0000000..df05075 --- /dev/null +++ b/bakanet/src/bakanet/sock_layer/ip_protocol.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace Bk::Net { + enum class IpProtocol + { + TCP = SOCK_STREAM, + UCP = SOCK_DGRAM, + }; +} \ No newline at end of file diff --git a/bakanet/src/bakanet/sock_layer/ip_version.h b/bakanet/src/bakanet/sock_layer/ip_version.h new file mode 100644 index 0000000..374363b --- /dev/null +++ b/bakanet/src/bakanet/sock_layer/ip_version.h @@ -0,0 +1,10 @@ +#pragma once + +namespace Bk::Net { + enum class IpVersion + { + UnSpec = AF_UNSPEC, + IPv4 = AF_INET, + IPv6 = AF_INET6, + }; +} \ No newline at end of file diff --git a/bakanet/src/bakanet/sock_layer/packet.h b/bakanet/src/bakanet/sock_layer/packet.h new file mode 100644 index 0000000..1f66f0c --- /dev/null +++ b/bakanet/src/bakanet/sock_layer/packet.h @@ -0,0 +1,57 @@ + +namespace Bk::Net { + class Packet + { + public: + Packet() = default; + Packet(std::vector data) + : payload(data) {} + + int size() { return payload.size(); } + + template + void push(const T& data) + { + static_assert(std::is_standard_layout::value, "Data is too complex to be pushed into vector"); + size_t i = payload.size(); + payload.resize(i + sizeof(T)); + std::memcpy(payload.data() + i, &data, sizeof(T)); + } + + template + void push(const T* data, int size) + { + for(int i = 0; i < size; i++) push(data[i]); + } + + template + T pull() + { + static_assert(std::is_standard_layout::value, "Data is too complex to be pulled from vector"); + T data; + size_t i = payload.size() - sizeof(T); + std::memcpy(&data, payload.data() + i, sizeof(T)); + payload.resize(i); + return data; + } + + template + std::unique_ptr pull(int size) + { + std::unique_ptr data(new T[size]); + for(int i = size - 1; i >= 0; i--) data[i] = pull(); + return data; + } + + bool append_data(std::vector data) + { + if (!data.size()) return false; + size_t i = payload.size(); + payload.resize(i + data.size()); + std::memcpy(payload.data() + i, data.data(), sizeof(char) * data.size()); + return true; + } + + std::vector payload; + }; +} \ No newline at end of file diff --git a/bakanet/src/bakanet/sock_layer/socket.h b/bakanet/src/bakanet/sock_layer/socket.h new file mode 100644 index 0000000..2a6c155 --- /dev/null +++ b/bakanet/src/bakanet/sock_layer/socket.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include "ip_address.h" +#include "ip_protocol.h" + +namespace Bk::Net { + + using Connection = int; + + class Socket + { + public: + virtual ~Socket() { } + + virtual bool init() = 0; + virtual bool start(int cpt_conn) = 0; + virtual Connection ack() = 0; + virtual bool conn() = 0; + virtual void emit(std::vector packet) = 0; + virtual void emit(Connection socket, std::vector packet) = 0; + virtual std::vector obtain(int size) = 0; + virtual std::vector obtain(Connection socket, int size) = 0; + + static std::unique_ptr Create(IpAddress ip, int port, IpProtocol proto); + }; +} \ No newline at end of file diff --git a/bakanet/src/bakanetpch.h b/bakanet/src/bakanetpch.h new file mode 100644 index 0000000..837feea --- /dev/null +++ b/bakanet/src/bakanetpch.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BK_PLAFORM_WINDOWS + #include + #include + #include + #pragma comment(lib,"WS2_32.lib") +#elif BK_PLAFORM_LINUX + #include + #include + #include + #include + #include + #include +#else + #error "Plaform not supported" +#endif diff --git a/bakanet/src/plaform/linux/linux_dns_lookup.cpp b/bakanet/src/plaform/linux/linux_dns_lookup.cpp new file mode 100644 index 0000000..70a1303 --- /dev/null +++ b/bakanet/src/plaform/linux/linux_dns_lookup.cpp @@ -0,0 +1,43 @@ +#include "dns_lookup.h" + +namespace Bk::Net { + std::vector dns_lookup(const std::string &host_name, IpVersion ipv) + { + std::vector output; + + struct addrinfo hints, *res, *p; + int status, ai_family; + char ip_address[INET6_ADDRSTRLEN]; + + ai_family = (int)ipv; + memset(&hints, 0, sizeof hints); + hints.ai_family = ai_family; + hints.ai_socktype = SOCK_STREAM; + + if ((status = getaddrinfo(host_name.c_str(), NULL, &hints, &res)) != 0) + { + output.push_back(""); + return output; + } + + for(p = res;p != NULL; p = p->ai_next) + { + void *addr; + if (p->ai_family == AF_INET) + { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; + addr = &(ipv4->sin_addr); + } else + { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; + addr = &(ipv6->sin6_addr); + } + inet_ntop(p->ai_family, addr, ip_address, sizeof ip_address); + output.push_back(ip_address); + } + + freeaddrinfo(res); // free the linked list + + return output; + } +} \ No newline at end of file diff --git a/bakanet/src/plaform/linux/linux_ip_address.cpp b/bakanet/src/plaform/linux/linux_ip_address.cpp new file mode 100644 index 0000000..ba70aa3 --- /dev/null +++ b/bakanet/src/plaform/linux/linux_ip_address.cpp @@ -0,0 +1,14 @@ +#include +#include + +namespace Bk::Net { + IpAddress::IpAddress(std::string ip, IpVersion ipv) + : str(ip), version(ipv) { } + + void* IpAddress::get_data() + { + struct in_addr addr; + if (inet_pton(AF_INET, str.c_str(), &addr) <= 0) perror("Bad IP"); + return (void*)addr; + } +} \ No newline at end of file diff --git a/bakanet/src/plaform/linux/linux_socket.cpp b/bakanet/src/plaform/linux/linux_socket.cpp new file mode 100644 index 0000000..7e460b2 --- /dev/null +++ b/bakanet/src/plaform/linux/linux_socket.cpp @@ -0,0 +1,93 @@ +#include "socket.h" +#include + +namespace Bk::Net { + LinuxSocket::LinuxSocket(IpAddress ip, int port, IpProtocol proto) + : ip_addr(ip), ip_proto(proto) + { + //LinuxSocket creation step + if ((socket_id = socket((int)ip_addr.version, (int)ip_proto, 0)) < 0) + { + perror("socket failed"); + exit(EXIT_FAILURE); + } + addr.sin_addr = (struct in_addr)ip_addr.get_data(); + addr.sin_family = (int)ip_addr.version; + addr.sin_port = htons(port); + } + + LinuxSocket::~LinuxSocket() + { + close(socket_id); + } + + bool LinuxSocket::init() + { + //Binding step + int status; + if ((status = bind(socket_id, (struct sockaddr*)&addr, sizeof(addr)) < 0)) + { + perror("bind failed"); + return false; + } + return true; + } + + bool LinuxSocket::start(int cpt_conn) + { + //Listening step + if (listen(socket_id, cpt_conn) < 0) + { + return false; + } + return true; + } + + Connection LinuxSocket::ack() + { + + socklen_t addrlen = sizeof(addr); + return accept(socket_id, (struct sockaddr*)&addr, &addrlen); + } + + bool LinuxSocket::conn() + { + if (connect(socket_id, (struct sockaddr*)&addr, sizeof(addr)) < 0) + { + return false; + } + return true; + } + + void LinuxSocket::emit(std::vector packet) + { + write(socket_id, packet.data(), packet.size()); + } + + void LinuxSocket::emit(Connection conn, std::vector packet) + { + write(conn, packet.data(), packet.size()); + } + + std::vector LinuxSocket::obtain(int size) + { + std::vector buffer; + buffer.resize(size); + int status = read(socket_id, buffer.data(), buffer.size() - 1); + return status > 0 ? buffer : std::vector(); + } + + std::vector LinuxSocket::obtain(Connection conn, int size) + { + std::vector buffer; + buffer.resize(size); + int read_size = read(conn, buffer.data(), buffer.size() - 1); + buffer.resize(read_size); + return buffer; + } + + std::unique_ptr Socket::Create(IpAddress ip, int port, IpProtocol proto) + { + return std::unique_ptr(new LinuxSocket(ip, port, proto)); + } +} \ No newline at end of file diff --git a/bakanet/src/plaform/linux/linux_socket.h b/bakanet/src/plaform/linux/linux_socket.h new file mode 100644 index 0000000..2602360 --- /dev/null +++ b/bakanet/src/plaform/linux/linux_socket.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace Bk::Net { + class LinuxSocket : public Socket + { + public: + LinuxSocket(IpAddress ip, int port, IpProtocol proto); + virtual ~LinuxSocket(); + + bool init() override; + bool start(int cpt_conn) override; + Connection ack() override; + bool conn() override; + void emit(std::vector packet) override; + void emit(Connection socket, std::vector packet) override; + std::vector obtain(int size) override; + std::vector obtain(Connection socket, int size) override; + + private: + Connection socket_id; + struct sockaddr_in addr; + IpAddress ip_addr; + IpProtocol ip_proto; + }; +} \ No newline at end of file diff --git a/bakanet/src/plaform/windows/windows_dns_lookup.cpp b/bakanet/src/plaform/windows/windows_dns_lookup.cpp new file mode 100644 index 0000000..e69de29 diff --git a/bakanet/src/plaform/windows/windows_ip_adress.cpp b/bakanet/src/plaform/windows/windows_ip_adress.cpp new file mode 100644 index 0000000..e69de29 diff --git a/bakanet/src/plaform/windows/windows_socket.cpp b/bakanet/src/plaform/windows/windows_socket.cpp new file mode 100644 index 0000000..e69de29 diff --git a/bakanet/src/plaform/windows/windows_socket.h b/bakanet/src/plaform/windows/windows_socket.h new file mode 100644 index 0000000..e69de29 diff --git a/build b/build new file mode 100644 index 0000000..1c7e1a6 --- /dev/null +++ b/build @@ -0,0 +1,3 @@ +clear +premake5 gmake2 +make \ No newline at end of file diff --git a/premake5.lua b/premake5.lua new file mode 100644 index 0000000..a57640d --- /dev/null +++ b/premake5.lua @@ -0,0 +1,71 @@ +workspace "socket_unix" + architecture "x64" + configurations { "Debug", "Release" } + startproject "server" + flags + { + "MultiProcessorCompile" + } + +outputdir = "%{cfg.system}-%{cfg.architecture}-%{cfg.buildcfg}" + +IncludeDirs = {} + +IncludeDirs["bakanet"] = "%{wks.location}/bakanet/src/" + +project "server" + location "./sandbox/server" + kind "ConsoleApp" + language "C++" + cppdialect "C++17" + systemversion "latest" + + targetdir("%{wks.location}/bin/" .. outputdir .. "/%{prj.name}") + objdir("%{wks.location}/bin-int/" .. outputdir .. "/%{prj.name}") + + includedirs + { + "%{IncludeDirs.bakanet}", + "./sandbox/" + } + + files + { + "%{prj.location}/**.h", + "%{prj.location}/**.cpp", + "./sandbox/commun.h" + } + + links + { + "bakanet" + } + + + +project "client" + location "./sandbox/client" + kind "ConsoleApp" + language "C++" + cppdialect "C++17" + systemversion "latest" + + targetdir("%{wks.location}/bin/" .. outputdir .. "/%{prj.name}") + objdir("%{wks.location}/bin-int/" .. outputdir .. "/%{prj.name}") + + includedirs + { + "%{IncludeDirs.bakanet}" + } + + files + { + "%{prj.location}/**.h", + "%{prj.location}/**.cpp", + "./sandbox/commun.h" + } + + links + { + "bakanet" + } \ No newline at end of file