diff --git a/CMakeLists.txt b/CMakeLists.txt index 068642bb8..342951f33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,8 +29,8 @@ set(HEADERS src/log/Log.h src/Mem.h src/net/Client.h + src/net/Id.h src/net/Job.h - src/net/JobId.h src/net/JobResult.h src/net/Network.h src/net/strategies/DonateStrategy.h diff --git a/src/api/Httpd.cpp b/src/api/Httpd.cpp index 996bc0079..a604b5de3 100644 --- a/src/api/Httpd.cpp +++ b/src/api/Httpd.cpp @@ -4,8 +4,8 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2016-2018 XMRig , * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,7 +45,19 @@ bool Httpd::start() return false; } - m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, m_port, nullptr, nullptr, &Httpd::handler, this, MHD_OPTION_END); + unsigned int flags = 0; + if (MHD_is_feature_supported(MHD_FEATURE_EPOLL)) { + flags = MHD_USE_EPOLL_LINUX_ONLY | MHD_USE_EPOLL_INTERNALLY_LINUX_ONLY; + } + else { + flags = MHD_USE_SELECT_INTERNALLY; + } + + if (MHD_is_feature_supported(MHD_FEATURE_IPv6)) { + flags |= MHD_USE_DUAL_STACK; + } + + m_daemon = MHD_start_daemon(flags, m_port, nullptr, nullptr, &Httpd::handler, this, MHD_OPTION_END); if (!m_daemon) { LOG_ERR("HTTP Daemon failed to start."); return false; diff --git a/src/api/Httpd.h b/src/api/Httpd.h index 7a4dd9321..30618e2a8 100644 --- a/src/api/Httpd.h +++ b/src/api/Httpd.h @@ -4,8 +4,8 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2016-2018 XMRig , * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/net/Client.cpp b/src/net/Client.cpp index eb07bf21a..d347ed22a 100644 --- a/src/net/Client.cpp +++ b/src/net/Client.cpp @@ -54,6 +54,7 @@ int64_t Client::m_sequence = 1; Client::Client(int id, const char *agent, IClientListener *listener) : + m_ipv6(false), m_quiet(false), m_agent(agent), m_listener(listener), @@ -71,7 +72,7 @@ Client::Client(int id, const char *agent, IClientListener *listener) : m_resolver.data = this; - m_hints.ai_family = PF_INET; + m_hints.ai_family = AF_UNSPEC; m_hints.ai_socktype = SOCK_STREAM; m_hints.ai_protocol = IPPROTO_TCP; @@ -109,19 +110,6 @@ void Client::connect(const Url *url) } -void Client::disconnect() -{ -# ifndef XMRIG_PROXY_PROJECT - uv_timer_stop(&m_keepAliveTimer); -# endif - - m_expire = 0; - m_failures = -1; - - close(); -} - - void Client::setUrl(const Url *url) { if (!url || !url->isValid()) { @@ -150,6 +138,19 @@ void Client::tick(uint64_t now) } +bool Client::disconnect() +{ +# ifndef XMRIG_PROXY_PROJECT + uv_timer_stop(&m_keepAliveTimer); +# endif + + m_expire = 0; + m_failures = -1; + + return close(); +} + + int64_t Client::submit(const JobResult &result) { # ifdef XMRIG_PROXY_PROJECT @@ -167,13 +168,29 @@ int64_t Client::submit(const JobResult &result) # endif const size_t size = snprintf(m_sendBuf, sizeof(m_sendBuf), "{\"id\":%" PRIu64 ",\"jsonrpc\":\"2.0\",\"method\":\"submit\",\"params\":{\"id\":\"%s\",\"job_id\":\"%s\",\"nonce\":\"%s\",\"result\":\"%s\"}}\n", - m_sequence, m_rpcId, result.jobId.data(), nonce, data); + m_sequence, m_rpcId.data(), result.jobId.data(), nonce, data); m_results[m_sequence] = SubmitResult(m_sequence, result.diff, result.actualDiff()); return send(size); } +bool Client::close() +{ + if (m_state == UnconnectedState || m_state == ClosingState || !m_socket) { + return false; + } + + setState(ClosingState); + + if (uv_is_closing(reinterpret_cast(m_socket)) == 0) { + uv_close(reinterpret_cast(m_socket), Client::onClose); + } + + return true; +} + + bool Client::isCriticalError(const char *message) { if (!message) { @@ -235,15 +252,11 @@ bool Client::parseJob(const rapidjson::Value ¶ms, int *code) bool Client::parseLogin(const rapidjson::Value &result, int *code) { - const char *id = result["id"].GetString(); - if (!id || strlen(id) >= sizeof(m_rpcId)) { + if (!m_rpcId.setId(result["id"].GetString())) { *code = 1; return false; } - memset(m_rpcId, 0, sizeof(m_rpcId)); - memcpy(m_rpcId, id, strlen(id)); - return parseJob(result["job"], code); } @@ -291,21 +304,25 @@ int64_t Client::send(size_t size) } -void Client::close() +void Client::connect(const std::vector &ipv4, const std::vector &ipv6) { - if (m_state == UnconnectedState || m_state == ClosingState || !m_socket) { - return; + addrinfo *addr = nullptr; + m_ipv6 = ipv4.empty() && !ipv6.empty(); + + if (m_ipv6) { + addr = ipv6[ipv6.size() == 1 ? 0 : rand() % ipv6.size()]; + uv_ip6_name(reinterpret_cast(addr->ai_addr), m_ip, 45); + } + else { + addr = ipv4[ipv4.size() == 1 ? 0 : rand() % ipv4.size()]; + uv_ip4_name(reinterpret_cast(addr->ai_addr), m_ip, 16); } - setState(ClosingState); - - if (uv_is_closing(reinterpret_cast(m_socket)) == 0) { - uv_close(reinterpret_cast(m_socket), Client::onClose); - } + connect(addr->ai_addr); } -void Client::connect(struct sockaddr *addr) +void Client::connect(sockaddr *addr) { setState(ConnectingState); @@ -374,6 +391,11 @@ void Client::parse(char *line, size_t len) LOG_DEBUG("[%s:%u] received (%d bytes): \"%s\"", m_url.host(), m_url.port(), len, line); + if (len < 32 || line[0] != '{') { + LOG_ERR("[%s:%u] JSON decode failed", m_url.host(), m_url.port()); + return; + } + rapidjson::Document doc; if (doc.ParseInsitu(line).HasParseError()) { if (!m_quiet) { @@ -456,7 +478,8 @@ void Client::parseResponse(int64_t id, const rapidjson::Value &result, const rap LOG_ERR("[%s:%u] login error code: %d", m_url.host(), m_url.port(), code); } - return close(); + close(); + return; } m_failures = 0; @@ -476,7 +499,7 @@ void Client::parseResponse(int64_t id, const rapidjson::Value &result, const rap void Client::ping() { - send(snprintf(m_sendBuf, sizeof(m_sendBuf), "{\"id\":%" PRId64 ",\"jsonrpc\":\"2.0\",\"method\":\"keepalived\",\"params\":{\"id\":\"%s\"}}\n", m_sequence, m_rpcId)); + send(snprintf(m_sendBuf, sizeof(m_sendBuf), "{\"id\":%" PRId64 ",\"jsonrpc\":\"2.0\",\"method\":\"keepalived\",\"params\":{\"id\":\"%s\"}}\n", m_sequence, m_rpcId.data())); } @@ -532,7 +555,7 @@ void Client::onAllocBuffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t auto client = getClient(handle->data); buf->base = &client->m_recvBuf.base[client->m_recvBufPos]; - buf->len = client->m_recvBuf.len - (unsigned long)client->m_recvBufPos; + buf->len = client->m_recvBuf.len - client->m_recvBufPos; } @@ -582,11 +605,13 @@ void Client::onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) LOG_ERR("[%s:%u] read error: \"%s\"", client->m_url.host(), client->m_url.port(), uv_strerror((int) nread)); } - return client->close(); + client->close(); + return; } if ((size_t) nread > (sizeof(m_buf) - 8 - client->m_recvBufPos)) { - return client->close(); + client->close(); + return; } client->m_recvBufPos += nread; @@ -628,24 +653,27 @@ void Client::onResolved(uv_getaddrinfo_t *req, int status, struct addrinfo *res) addrinfo *ptr = res; std::vector ipv4; + std::vector ipv6; while (ptr != nullptr) { if (ptr->ai_family == AF_INET) { ipv4.push_back(ptr); } + if (ptr->ai_family == AF_INET6) { + ipv6.push_back(ptr); + } + ptr = ptr->ai_next; } - if (ipv4.empty()) { - LOG_ERR("[%s:%u] DNS error: \"No IPv4 records found\"", client->m_url.host(), client->m_url.port()); + if (ipv4.empty() && ipv6.empty()) { + LOG_ERR("[%s:%u] DNS error: \"No IPv4 (A) or IPv6 (AAAA) records found\"", client->m_url.host(), client->m_url.port()); + + uv_freeaddrinfo(res); return client->reconnect(); } - ptr = ipv4[rand() % ipv4.size()]; - - uv_ip4_name(reinterpret_cast(ptr->ai_addr), client->m_ip, 16); - - client->connect(ptr->ai_addr); + client->connect(ipv4, ipv6); uv_freeaddrinfo(res); } diff --git a/src/net/Client.h b/src/net/Client.h index 2cc169153..db4804616 100644 --- a/src/net/Client.h +++ b/src/net/Client.h @@ -27,8 +27,10 @@ #include #include +#include +#include "net/Id.h" #include "net/Job.h" #include "net/SubmitResult.h" #include "net/Url.h" @@ -56,10 +58,10 @@ public: Client(int id, const char *agent, IClientListener *listener); ~Client(); + bool disconnect(); int64_t submit(const JobResult &result); void connect(); void connect(const Url *url); - void disconnect(); void setUrl(const Url *url); void tick(uint64_t now); @@ -74,13 +76,14 @@ public: inline void setRetryPause(int ms) { m_retryPause = ms; } private: + bool close(); bool isCriticalError(const char *message); bool parseJob(const rapidjson::Value ¶ms, int *code); bool parseLogin(const rapidjson::Value &result, int *code); int resolve(const char *host); int64_t send(size_t size); - void close(); - void connect(struct sockaddr *addr); + void connect(const std::vector &ipv4, const std::vector &ipv6); + void connect(sockaddr *addr); void login(); void parse(char *line, size_t len); void parseNotification(const char *method, const rapidjson::Value ¶ms, const rapidjson::Value &error); @@ -99,10 +102,10 @@ private: static inline Client *getClient(void *data) { return static_cast(data); } addrinfo m_hints; + bool m_ipv6; bool m_quiet; char m_buf[2048]; - char m_ip[17]; - char m_rpcId[64]; + char m_ip[46]; char m_sendBuf[768]; const char *m_agent; IClientListener *m_listener; @@ -120,6 +123,7 @@ private: uv_getaddrinfo_t m_resolver; uv_stream_t *m_stream; uv_tcp_t *m_socket; + xmrig::Id m_rpcId; # ifndef XMRIG_PROXY_PROJECT uv_timer_t m_keepAliveTimer; diff --git a/src/net/JobId.h b/src/net/Id.h similarity index 75% rename from src/net/JobId.h rename to src/net/Id.h index 061897792..5c77d1d43 100644 --- a/src/net/JobId.h +++ b/src/net/Id.h @@ -4,8 +4,8 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2016-2018 XMRig , * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,40 +21,51 @@ * along with this program. If not, see . */ -#ifndef __JOBID_H__ -#define __JOBID_H__ +#ifndef __ID_H__ +#define __ID_H__ #include -class JobId +namespace xmrig { + + +class Id { public: - inline JobId() + inline Id() { memset(m_data, 0, sizeof(m_data)); } - inline JobId(const char *id, size_t sizeFix = 0) + inline Id(const char *id, size_t sizeFix = 0) { setId(id, sizeFix); } - inline bool operator==(const JobId &other) const + inline bool operator==(const Id &other) const { return memcmp(m_data, other.m_data, sizeof(m_data)) == 0; } - inline bool operator!=(const JobId &other) const + inline bool operator!=(const Id &other) const { return memcmp(m_data, other.m_data, sizeof(m_data)) != 0; } + Id &operator=(const Id &other) + { + memcpy(m_data, other.m_data, sizeof(m_data)); + + return *this; + } + + inline bool setId(const char *id, size_t sizeFix = 0) { memset(m_data, 0, sizeof(m_data)); @@ -80,4 +91,8 @@ private: char m_data[64]; }; -#endif /* __JOBID_H__ */ + +} /* namespace xmrig */ + + +#endif /* __ID_H__ */ diff --git a/src/net/Job.h b/src/net/Job.h index d74b64edd..e2fe9f166 100644 --- a/src/net/Job.h +++ b/src/net/Job.h @@ -31,7 +31,7 @@ #include "align.h" -#include "net/JobId.h" +#include "net/Id.h" class Job @@ -46,9 +46,9 @@ public: inline bool isNicehash() const { return m_nicehash; } inline bool isValid() const { return m_size > 0 && m_diff > 0; } inline bool setId(const char *id) { return m_id.setId(id); } - inline const JobId &id() const { return m_id; } inline const uint32_t *nonce() const { return reinterpret_cast(m_blob + 39); } inline const uint8_t *blob() const { return m_blob; } + inline const xmrig::Id &id() const { return m_id; } inline int poolId() const { return m_poolId; } inline int threadId() const { return m_threadId; } inline size_t size() const { return m_size; } @@ -77,10 +77,10 @@ private: bool m_nicehash; int m_poolId; int m_threadId; - JobId m_id; size_t m_size; uint64_t m_diff; uint64_t m_target; + xmrig::Id m_id; # ifdef XMRIG_PROXY_PROJECT VAR_ALIGN(16, char m_rawBlob[169]); diff --git a/src/net/JobResult.h b/src/net/JobResult.h index 520022a76..e32825847 100644 --- a/src/net/JobResult.h +++ b/src/net/JobResult.h @@ -4,8 +4,8 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2016-2018 XMRig , * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,11 +36,11 @@ class JobResult { public: inline JobResult() : poolId(0), diff(0), nonce(0) {} - inline JobResult(int poolId, const JobId &jobId, uint32_t nonce, const uint8_t *result, uint32_t diff) : + inline JobResult(int poolId, const xmrig::Id &jobId, uint32_t nonce, const uint8_t *result, uint32_t diff) : poolId(poolId), - jobId(jobId), diff(diff), - nonce(nonce) + nonce(nonce), + jobId(jobId) { memcpy(this->result, result, sizeof(this->result)); } @@ -71,10 +71,10 @@ public: int poolId; - JobId jobId; uint32_t diff; uint32_t nonce; uint8_t result[32]; + xmrig::Id jobId; }; #endif /* __JOBRESULT_H__ */ diff --git a/src/net/Url.cpp b/src/net/Url.cpp index dcbe82af5..f58ca48af 100644 --- a/src/net/Url.cpp +++ b/src/net/Url.cpp @@ -4,8 +4,8 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2016-2018 XMRig , * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,6 +41,7 @@ Url::Url() : m_host(nullptr), m_password(nullptr), m_user(nullptr), + m_url(nullptr), m_port(kDefaultPort) { } @@ -63,6 +64,7 @@ Url::Url(const char *url) : m_host(nullptr), m_password(nullptr), m_user(nullptr), + m_url(nullptr), m_port(kDefaultPort) { parse(url); @@ -74,6 +76,7 @@ Url::Url(const char *host, uint16_t port, const char *user, const char *password m_nicehash(nicehash), m_password(password ? strdup(password) : nullptr), m_user(user ? strdup(user) : nullptr), + m_url(nullptr), m_port(port) { m_host = strdup(host); @@ -85,6 +88,10 @@ Url::~Url() free(m_host); free(m_password); free(m_user); + + if (m_url) { + delete [] m_url; + } } @@ -105,6 +112,10 @@ bool Url::parse(const char *url) return false; } + if (base[0] == '[') { + return parseIPv6(base); + } + const char *port = strchr(base, ':'); if (!port) { m_host = strdup(base); @@ -112,9 +123,8 @@ bool Url::parse(const char *url) } const size_t size = port++ - base + 1; - m_host = static_cast(malloc(size)); + m_host = new char[size](); memcpy(m_host, base, size - 1); - m_host[size - 1] = '\0'; m_port = (uint16_t) strtol(port, nullptr, 10); return true; @@ -139,6 +149,19 @@ bool Url::setUserpass(const char *userpass) } +const char *Url::url() const +{ + if (!m_url) { + const size_t size = strlen(m_host) + 8; + m_url = new char[size]; + + snprintf(m_url, size - 1, "%s:%d", m_host, m_port); + } + + return m_url; +} + + void Url::applyExceptions() { if (!isValid()) { @@ -178,6 +201,20 @@ void Url::setUser(const char *user) } +bool Url::operator==(const Url &other) const +{ + if (m_port != other.m_port || m_keepAlive != other.m_keepAlive || m_nicehash != other.m_nicehash) { + return false; + } + + if (strcmp(host(), other.host()) != 0 || strcmp(user(), other.user()) != 0 || strcmp(password(), other.password()) != 0) { + return false; + } + + return true; +} + + Url &Url::operator=(const Url *other) { m_keepAlive = other->m_keepAlive; @@ -192,3 +229,25 @@ Url &Url::operator=(const Url *other) return *this; } + + +bool Url::parseIPv6(const char *addr) +{ + const char *end = strchr(addr, ']'); + if (!end) { + return false; + } + + const char *port = strchr(end, ':'); + if (!port) { + return false; + } + + const size_t size = end - addr; + m_host = new char[size](); + memcpy(m_host, addr + 1, size - 1); + + m_port = (uint16_t) strtol(port + 1, nullptr, 10); + + return true; +} diff --git a/src/net/Url.h b/src/net/Url.h index a1982300c..330f2d8e7 100644 --- a/src/net/Url.h +++ b/src/net/Url.h @@ -4,8 +4,8 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2016-2018 XMRig , * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,18 +52,23 @@ public: bool parse(const char *url); bool setUserpass(const char *userpass); + const char *url() const; void applyExceptions(); void setPassword(const char *password); void setUser(const char *user); + bool operator==(const Url &other) const; Url &operator=(const Url *other); private: + bool parseIPv6(const char *addr); + bool m_keepAlive; bool m_nicehash; char *m_host; char *m_password; char *m_user; + mutable char *m_url; uint16_t m_port; };