Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build/
.vscode
.ipynb_checkpoints/

.pio/
10 changes: 10 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}
2 changes: 2 additions & 0 deletions ArduinoCore-Linux/cores/arduino/Arduino.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
#pragma once

#include "Platform.h"

/// do not use exithandler atexit
#ifndef HOST
# define HOST
Expand Down
8 changes: 8 additions & 0 deletions ArduinoCore-Linux/cores/arduino/DesktopSocket.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once
#include "Platform.h"

#if ARDUINO_EMULATOR_WINDOWS
#include "DesktopSocketWindows.h"
#else
#include "DesktopSocketPosix.h"
#endif
47 changes: 47 additions & 0 deletions ArduinoCore-Linux/cores/arduino/DesktopSocketPosix.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once

#include <arpa/inet.h>
// Collide with existing definitions
#undef INADDR_NONE

#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

namespace arduino {

using SocketHandle = int;
constexpr SocketHandle INVALID_SOCKET_HANDLE = -1;
using SocketLength = socklen_t;

class SocketRuntime {
public:
SocketRuntime() = default;
~SocketRuntime() = default;
};

inline void ensureSocketRuntime() {
static SocketRuntime runtime;
(void)runtime;
}

inline void closeSocket(SocketHandle socket) { ::close(socket); }
inline int socketLastError() { return errno; }

inline bool socketWouldBlock(int error) {
return error == EWOULDBLOCK || error == EAGAIN;
}
inline bool socketConnectionReset(int error) { return error == ECONNRESET; }

inline bool setSocketNonBlocking(SocketHandle socket, bool enabled = true) {
int flags = fcntl(socket, F_GETFL, 0);
return flags >= 0 && fcntl(socket, F_SETFL, enabled ? flags | O_NONBLOCK
: flags & ~O_NONBLOCK) == 0;
}

} // namespace arduino
46 changes: 46 additions & 0 deletions ArduinoCore-Linux/cores/arduino/DesktopSocketWindows.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#define INPUT ARDUINO_WINDOWS_INPUT
#include <winsock2.h>
#include <ws2tcpip.h>
#undef INPUT
#undef INADDR_NONE

#ifndef SHUT_RDWR
#define SHUT_RDWR SD_BOTH
#endif

namespace arduino {

using SocketHandle = SOCKET;
constexpr SocketHandle INVALID_SOCKET_HANDLE = INVALID_SOCKET;
using SocketLength = int;

class SocketRuntime {
public:
SocketRuntime() {
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
}
~SocketRuntime() { WSACleanup(); }
};

inline void ensureSocketRuntime() {
static SocketRuntime runtime;
(void)runtime;
}

inline void closeSocket(SocketHandle socket) { closesocket(socket); }
inline int socketLastError() { return WSAGetLastError(); }
inline bool socketWouldBlock(int error) { return error == WSAEWOULDBLOCK; }
inline bool socketConnectionReset(int error) { return error == WSAECONNRESET; }

inline bool setSocketNonBlocking(SocketHandle socket, bool enabled = true) {
u_long mode = enabled ? 1UL : 0UL;
return ioctlsocket(socket, FIONBIO, &mode) == 0;
}

} // namespace arduino
16 changes: 10 additions & 6 deletions ArduinoCore-Linux/cores/arduino/Ethernet.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@
*/
#pragma once

#include <arpa/inet.h> // for inet_pton
#include <netdb.h> // for gethostbyname, struct hostent
#include <unistd.h> // for close
#include "DesktopSocket.h"
#include <cstring>
#include <memory> // This is the include you need

#include "ArduinoLogger.h"
Expand Down Expand Up @@ -351,12 +350,17 @@ class EthernetClient : public Client {
serv_addr4.sin_port = htons(port);
if (::inet_pton(AF_INET, address, &serv_addr4.sin_addr) <= 0) {
// Not an IP, try to resolve hostname
struct hostent* he = ::gethostbyname(address);
if (he == nullptr || he->h_addr_list[0] == nullptr) {
addrinfo hints{};
hints.ai_family = AF_INET;
addrinfo* info = nullptr;
int rc = ::getaddrinfo(address, nullptr, &hints, &info);
if (rc != 0 || info == nullptr) {
Logger.error(WIFICLIENT, "Hostname resolution failed");
serv_addr4.sin_addr.s_addr = 0;
} else {
memcpy(&serv_addr4.sin_addr, he->h_addr_list[0], he->h_length);
auto* resolved = reinterpret_cast<sockaddr_in*>(info->ai_addr);
memcpy(&serv_addr4.sin_addr, &resolved->sin_addr, sizeof(serv_addr4.sin_addr));
freeaddrinfo(info);
}
}
return IPAddress(serv_addr4.sin_addr.s_addr);
Expand Down
196 changes: 6 additions & 190 deletions ArduinoCore-Linux/cores/arduino/EthernetServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,194 +19,10 @@
*/
#pragma once

#include <poll.h>
#include <unistd.h>
#include "Platform.h"

#include "Ethernet.h"
#include "api/Server.h"
#include "SignalHandler.h"

namespace arduino {

/**
* A minimal ethernet server
*/

class EthernetServer : public Server {
private:
uint16_t _port;
int server_fd = 0;
struct sockaddr_in server_addr;
int _status = wl_status_t::WL_DISCONNECTED;
bool is_blocking = false;
bool _noDelay = false;

static std::vector<EthernetServer*>& active_servers() {
static std::vector<EthernetServer*> servers;
return servers;
}
static void cleanupAll(int sig) {
for (auto* server : active_servers()) {
if (server && server->server_fd > 0) {
shutdown(server->server_fd, SHUT_RDWR);
close(server->server_fd);
server->server_fd = 0;
}
}
}

public:
EthernetServer(int port = 80) {
_port = port;
// Register signal handler only once
static bool signal_registered = false;
if (!signal_registered) {
SignalHandler::registerHandler(SIGINT, cleanupAll);
SignalHandler::registerHandler(SIGTERM, cleanupAll);
signal_registered = true;
}
}

~EthernetServer() {
stop();
// Remove from active servers list
auto& servers = active_servers();
auto it = std::find(servers.begin(), servers.end(), this);
if (it != servers.end()) {
servers.erase(it);
}
}
void begin() { begin(_port); }
void begin(int port) { begin_(port); }

void stop() {
if (server_fd > 0) {
// Set SO_LINGER to force immediate close
struct linger linger_opt = {1, 0};
setsockopt(server_fd, SOL_SOCKET, SO_LINGER, &linger_opt,
sizeof(linger_opt));

shutdown(server_fd, SHUT_RDWR);
close(server_fd);
}
server_fd = 0;
_status = wl_status_t::WL_DISCONNECTED;
}
WiFiClient accept() { return available_(); }
WiFiClient available(uint8_t* status = NULL) { return available_(); }
virtual size_t write(uint8_t ch) { return write(&ch, 1); }
virtual size_t write(const uint8_t* buf, size_t size) {
int rc = ::write(server_fd, buf, size);
if (rc < 0) {
rc = 0;
}
return rc;
}
int status() { return _status; }

// The following are for compatibility with ESP32 WiFiServer
// get/setNoDelay maintain the state variable, but otherwise
// have no effect on the code.
void setNoDelay(bool nodelay) { _noDelay = nodelay; }
bool getNoDelay() { return _noDelay; }
bool hasClient() {
if (server_fd <= 0) return false;
struct pollfd pfd;
pfd.fd = server_fd;
pfd.events = POLLIN;
return ::poll(&pfd, 1, 0) > 0 && (pfd.revents & POLLIN);
}

using Print::write;

protected:

bool begin_(int port = 0) {
if (port > 0) _port = port;
_status = wl_status_t::WL_DISCONNECTED;

// create server socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
// error("socket failed");
_status = wl_status_t::WL_CONNECT_FAILED;
return false;
}

// Reuse address after restart
int iSetOption = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&iSetOption,
sizeof(iSetOption));

// Set SO_REUSEPORT for better port reuse
setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, (char*)&iSetOption,
sizeof(iSetOption));

// config socket
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(_port);

// bind socket to port
while (::bind(server_fd, (struct sockaddr*)&server_addr,
sizeof(server_addr)) < 0) {
// error("bind failed");
//_status = wl_status_t::WL_CONNECT_FAILED;
Logger.error("bind failed");
// return false;
delay(1000);
}

// listen for connections
if (::listen(server_fd, 10) < 0) {
// error("listen failed");
_status = wl_status_t::WL_CONNECT_FAILED;
Logger.error("listen failed");
return false;
}

_noDelay = false;

// Add to active servers list for signal handling
active_servers().push_back(this);
_status = wl_status_t::WL_CONNECTED;
return true;
}

void setBlocking(bool flag) { is_blocking = flag; }

EthernetClient available_() {
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int client_fd;

if (_status == wl_status_t::WL_CONNECT_FAILED) {
begin(_port);
}

struct pollfd pfd;
pfd.fd = server_fd;
pfd.events = POLLIN;
int poll_rc = ::poll(&pfd, 1, 200);

// non blocking check if we have any request to accept
if (!is_blocking) {
if (poll_rc <= 0 || !(pfd.revents & POLLIN)) {
EthernetClient result(nullptr);
return result;
}
}

// accept client connection (blocking call)
if ((client_fd = ::accept(server_fd, (struct sockaddr*)&client_addr,
&client_addr_len)) < 0) {
EthernetClient result(nullptr);
Logger.error("accept failed");
return result;
}
std::shared_ptr<SocketImpl> sock_impl = std::make_shared<SocketImpl>(client_fd, (struct sockaddr_in*)&client_addr);
EthernetClient result{sock_impl};
return result;
}
};

} // namespace arduino
#if ARDUINO_EMULATOR_WINDOWS
#include "EthernetServerWindows.h"
#else
#include "EthernetServerPosix.h"
#endif
Loading
Loading