diff --git a/bsnes/target-bsnes/bsnes.cpp b/bsnes/target-bsnes/bsnes.cpp index c7bb3dd3..f27920f7 100644 --- a/bsnes/target-bsnes/bsnes.cpp +++ b/bsnes/target-bsnes/bsnes.cpp @@ -1,4 +1,6 @@ #include "bsnes.hpp" +#include + #include Video video; Audio audio; diff --git a/bsnes/target-bsnes/bsnes.hpp b/bsnes/target-bsnes/bsnes.hpp index 6f526bde..e5401da3 100644 --- a/bsnes/target-bsnes/bsnes.hpp +++ b/bsnes/target-bsnes/bsnes.hpp @@ -19,8 +19,9 @@ extern unique_pointer emulator; #include #include #include -#include #include +#include +#include #include "program/program.hpp" #include "input/input.hpp" diff --git a/nall/http/server.cpp b/nall/http/server.cpp new file mode 100644 index 00000000..8786775c --- /dev/null +++ b/nall/http/server.cpp @@ -0,0 +1,226 @@ +namespace nall::HTTP { + +auto Server::open(uint port, const string &serviceName, const string &command) -> bool +{ + if (serviceName) + { + if (!service::command(serviceName, command)) + return false; + } + + fd4 = socket(AF_INET, SOCK_STREAM, 0); + fd6 = socket(AF_INET6, SOCK_STREAM, 0); + if (!ipv4() && !ipv6()) + return false; + + { +#if defined(SO_RCVTIMEO) + if (settings.timeoutReceive) + { + struct timeval rcvtimeo; + rcvtimeo.tv_sec = settings.timeoutReceive / 1000; + rcvtimeo.tv_usec = settings.timeoutReceive % 1000 * 1000; + if (ipv4()) + setsockopt(fd4, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo, sizeof(struct timeval)); + if (ipv6()) + setsockopt(fd6, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo, sizeof(struct timeval)); + } +#endif + +#if defined(SO_SNDTIMEO) + if (settings.timeoutSend) + { + struct timeval sndtimeo; + sndtimeo.tv_sec = settings.timeoutSend / 1000; + sndtimeo.tv_usec = settings.timeoutSend % 1000 * 1000; + if (ipv4()) + setsockopt(fd4, SOL_SOCKET, SO_SNDTIMEO, &sndtimeo, sizeof(struct timeval)); + if (ipv6()) + setsockopt(fd6, SOL_SOCKET, SO_SNDTIMEO, &sndtimeo, sizeof(struct timeval)); + } +#endif + +#if defined(SO_NOSIGPIPE) // BSD, OSX + int nosigpipe = 1; + if (ipv4()) + setsockopt(fd4, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(int)); + if (ipv6()) + setsockopt(fd6, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(int)); +#endif + +#if defined(SO_REUSEADDR) // BSD, Linux, OSX + int reuseaddr = 1; + if (ipv4()) + setsockopt(fd4, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)); + if (ipv6()) + setsockopt(fd6, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)); +#endif + +#if defined(SO_REUSEPORT) // BSD, OSX + int reuseport = 1; + if (ipv4()) + setsockopt(fd4, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(int)); + if (ipv6()) + setsockopt(fd6, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(int)); +#endif + } + + addrin4.sin_family = AF_INET; + addrin4.sin_addr.s_addr = htonl(INADDR_ANY); + addrin4.sin_port = htons(port); + + addrin6.sin6_family = AF_INET6; + addrin6.sin6_addr = in6addr_any; + addrin6.sin6_port = htons(port); + + if (bind(fd4, (struct sockaddr *)&addrin4, sizeof(addrin4)) < 0 || listen(fd4, SOMAXCONN) < 0) + ipv4_close(); + if (bind(fd6, (struct sockaddr *)&addrin6, sizeof(addrin6)) < 0 || listen(fd6, SOMAXCONN) < 0) + ipv6_close(); + return ipv4() || ipv6(); +} + +auto Server::main(const function &function) -> void +{ + callback = function; +} + +auto Server::scan() -> string +{ + if (auto command = service::receive()) + return command; + if (connections >= settings.connectionLimit) + return "busy"; + if (ipv4() && ipv4_scan()) + return "ok"; + if (ipv6() && ipv6_scan()) + return "ok"; + return "idle"; +} + +auto Server::ipv4_scan() -> bool +{ + struct pollfd query = {0}; + query.fd = fd4; + query.events = POLLIN; + poll(&query, 1, 0); + + if (query.fd == fd4 && query.revents & POLLIN) + { + ++connections; + + thread::create([&](uintptr) + { + thread::detach(); + + int clientfd = -1; + struct sockaddr_in settings = {0}; + socklen_t socklen = sizeof(sockaddr_in); + + clientfd = accept(fd4, (struct sockaddr*)&settings, &socklen); + if(clientfd < 0) return; + + uint32_t ip = ntohl(settings.sin_addr.s_addr); + + Request request; + request._ipv6 = false; + request._ip = { + (uint8_t)(ip >> 24), ".", + (uint8_t)(ip >> 16), ".", + (uint8_t)(ip >> 8), ".", + (uint8_t)(ip >> 0) + }; + + if(download(clientfd, request) && callback) { + auto response = callback(request); + upload(clientfd, response); + } else { + upload(clientfd, Response()); //"501 Not Implemented" + } + + ::close(clientfd); + --connections; }, + 0, settings.threadStackSize); + + return true; + } + + return false; +} + +auto Server::ipv6_scan() -> bool +{ + struct pollfd query = {0}; + query.fd = fd6; + query.events = POLLIN; + poll(&query, 1, 0); + + if (query.fd == fd6 && query.revents & POLLIN) + { + ++connections; + + thread::create([&](uintptr) + { + thread::detach(); + + int clientfd = -1; + struct sockaddr_in6 settings = {0}; + socklen_t socklen = sizeof(sockaddr_in6); + + clientfd = accept(fd6, (struct sockaddr*)&settings, &socklen); + if(clientfd < 0) return; + + uint8_t* ip = settings.sin6_addr.s6_addr; + uint16_t ipSegment[8]; + for(auto n : range(8)) ipSegment[n] = ip[n * 2 + 0] * 256 + ip[n * 2 + 1]; + + Request request; + request._ipv6 = true; + //RFC5952 IPv6 encoding: the first longest 2+ consecutive zero-sequence is compressed to "::" + int zeroOffset = -1; + int zeroLength = 0; + int zeroCounter = 0; + for(auto n : range(8)) { + uint16_t value = ipSegment[n]; + if(value == 0) zeroCounter++; + if(zeroCounter > zeroLength) { + zeroLength = zeroCounter; + zeroOffset = 1 + n - zeroLength; + } + if(value != 0) zeroCounter = 0; + } + if(zeroLength == 1) zeroOffset = -1; + for(uint n = 0; n < 8;) { + if(n == zeroOffset) { + request._ip.append(n == 0 ? "::" : ":"); + n += zeroLength; + } else { + uint16_t value = ipSegment[n]; + request._ip.append(hex(value), n++ != 7 ? ":" : ""); + } + } + + if(download(clientfd, request) && callback) { + auto response = callback(request); + upload(clientfd, response); + } else { + upload(clientfd, Response()); //"501 Not Implemented" + } + + ::close(clientfd); + --connections; }, + 0, settings.threadStackSize); + + return true; + } + + return false; +} + +auto Server::close() -> void +{ + ipv4_close(); + ipv6_close(); +} + +} \ No newline at end of file diff --git a/nall/http/server.hpp b/nall/http/server.hpp index 3454ccf3..ef3071f0 100755 --- a/nall/http/server.hpp +++ b/nall/http/server.hpp @@ -6,10 +6,10 @@ namespace nall::HTTP { struct Server : Role, service { - inline auto open(uint port = 8080, const string& serviceName = "", const string& command = "") -> bool; - inline auto main(const function& function = {}) -> void; - inline auto scan() -> string; - inline auto close() -> void; + auto open(uint port = 8080, const string& serviceName = "", const string& command = "") -> bool; + auto main(const function& function = {}) -> void; + auto scan() -> string; + auto close() -> void; ~Server() { close(); } private: @@ -31,196 +31,4 @@ private: auto ipv6_scan() -> bool; }; -auto Server::open(uint port, const string& serviceName, const string& command) -> bool { - if(serviceName) { - if(!service::command(serviceName, command)) return false; - } - - fd4 = socket(AF_INET, SOCK_STREAM, 0); - fd6 = socket(AF_INET6, SOCK_STREAM, 0); - if(!ipv4() && !ipv6()) return false; - - { - #if defined(SO_RCVTIMEO) - if(settings.timeoutReceive) { - struct timeval rcvtimeo; - rcvtimeo.tv_sec = settings.timeoutReceive / 1000; - rcvtimeo.tv_usec = settings.timeoutReceive % 1000 * 1000; - if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo, sizeof(struct timeval)); - if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo, sizeof(struct timeval)); - } - #endif - - #if defined(SO_SNDTIMEO) - if(settings.timeoutSend) { - struct timeval sndtimeo; - sndtimeo.tv_sec = settings.timeoutSend / 1000; - sndtimeo.tv_usec = settings.timeoutSend % 1000 * 1000; - if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_SNDTIMEO, &sndtimeo, sizeof(struct timeval)); - if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_SNDTIMEO, &sndtimeo, sizeof(struct timeval)); - } - #endif - - #if defined(SO_NOSIGPIPE) //BSD, OSX - int nosigpipe = 1; - if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(int)); - if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(int)); - #endif - - #if defined(SO_REUSEADDR) //BSD, Linux, OSX - int reuseaddr = 1; - if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)); - if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)); - #endif - - #if defined(SO_REUSEPORT) //BSD, OSX - int reuseport = 1; - if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(int)); - if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(int)); - #endif - } - - addrin4.sin_family = AF_INET; - addrin4.sin_addr.s_addr = htonl(INADDR_ANY); - addrin4.sin_port = htons(port); - - addrin6.sin6_family = AF_INET6; - addrin6.sin6_addr = in6addr_any; - addrin6.sin6_port = htons(port); - - if(bind(fd4, (struct sockaddr*)&addrin4, sizeof(addrin4)) < 0 || listen(fd4, SOMAXCONN) < 0) ipv4_close(); - if(bind(fd6, (struct sockaddr*)&addrin6, sizeof(addrin6)) < 0 || listen(fd6, SOMAXCONN) < 0) ipv6_close(); - return ipv4() || ipv6(); -} - -auto Server::main(const function& function) -> void { - callback = function; -} - -auto Server::scan() -> string { - if(auto command = service::receive()) return command; - if(connections >= settings.connectionLimit) return "busy"; - if(ipv4() && ipv4_scan()) return "ok"; - if(ipv6() && ipv6_scan()) return "ok"; - return "idle"; -} - -auto Server::ipv4_scan() -> bool { - struct pollfd query = {0}; - query.fd = fd4; - query.events = POLLIN; - poll(&query, 1, 0); - - if(query.fd == fd4 && query.revents & POLLIN) { - ++connections; - - thread::create([&](uintptr) { - thread::detach(); - - int clientfd = -1; - struct sockaddr_in settings = {0}; - socklen_t socklen = sizeof(sockaddr_in); - - clientfd = accept(fd4, (struct sockaddr*)&settings, &socklen); - if(clientfd < 0) return; - - uint32_t ip = ntohl(settings.sin_addr.s_addr); - - Request request; - request._ipv6 = false; - request._ip = { - (uint8_t)(ip >> 24), ".", - (uint8_t)(ip >> 16), ".", - (uint8_t)(ip >> 8), ".", - (uint8_t)(ip >> 0) - }; - - if(download(clientfd, request) && callback) { - auto response = callback(request); - upload(clientfd, response); - } else { - upload(clientfd, Response()); //"501 Not Implemented" - } - - ::close(clientfd); - --connections; - }, 0, settings.threadStackSize); - - return true; - } - - return false; -} - -auto Server::ipv6_scan() -> bool { - struct pollfd query = {0}; - query.fd = fd6; - query.events = POLLIN; - poll(&query, 1, 0); - - if(query.fd == fd6 && query.revents & POLLIN) { - ++connections; - - thread::create([&](uintptr) { - thread::detach(); - - int clientfd = -1; - struct sockaddr_in6 settings = {0}; - socklen_t socklen = sizeof(sockaddr_in6); - - clientfd = accept(fd6, (struct sockaddr*)&settings, &socklen); - if(clientfd < 0) return; - - uint8_t* ip = settings.sin6_addr.s6_addr; - uint16_t ipSegment[8]; - for(auto n : range(8)) ipSegment[n] = ip[n * 2 + 0] * 256 + ip[n * 2 + 1]; - - Request request; - request._ipv6 = true; - //RFC5952 IPv6 encoding: the first longest 2+ consecutive zero-sequence is compressed to "::" - int zeroOffset = -1; - int zeroLength = 0; - int zeroCounter = 0; - for(auto n : range(8)) { - uint16_t value = ipSegment[n]; - if(value == 0) zeroCounter++; - if(zeroCounter > zeroLength) { - zeroLength = zeroCounter; - zeroOffset = 1 + n - zeroLength; - } - if(value != 0) zeroCounter = 0; - } - if(zeroLength == 1) zeroOffset = -1; - for(uint n = 0; n < 8;) { - if(n == zeroOffset) { - request._ip.append(n == 0 ? "::" : ":"); - n += zeroLength; - } else { - uint16_t value = ipSegment[n]; - request._ip.append(hex(value), n++ != 7 ? ":" : ""); - } - } - - if(download(clientfd, request) && callback) { - auto response = callback(request); - upload(clientfd, response); - } else { - upload(clientfd, Response()); //"501 Not Implemented" - } - - ::close(clientfd); - --connections; - }, 0, settings.threadStackSize); - - return true; - } - - return false; -} - -auto Server::close() -> void { - ipv4_close(); - ipv6_close(); -} - }