diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index f40cdda5..0d97a9a7 100755 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -24,16 +24,13 @@ #include #include #include -#include #include -#include #ifdef HAVE_LINUX_SERIAL # include #endif #ifdef HAVE_FREEBSD_UFTDI # include #endif -#include #ifdef HAVE_PPOLL # include #endif @@ -44,6 +41,7 @@ #include #include #include "lib/ebus/data.h" +#include "lib/utils/tcpsocket.h" namespace ebusd { @@ -874,46 +872,10 @@ result_t NetworkDevice::open() { if (result != RESULT_OK) { return result; } - struct sockaddr_in address; - memset(reinterpret_cast(&address), 0, sizeof(address)); - if (inet_aton(m_hostOrIp, &address.sin_addr) == 0) { - struct hostent* h = gethostbyname(m_hostOrIp); - if (h == nullptr) { - return RESULT_ERR_GENERIC_IO; // invalid host - } - memcpy(&address.sin_addr, h->h_addr_list[0], h->h_length); - } - address.sin_family = AF_INET; - address.sin_port = (in_port_t)htons(m_port); - - m_fd = socket(AF_INET, m_udp ? SOCK_DGRAM : SOCK_STREAM, 0); + m_fd = socketConnect(m_hostOrIp, m_port, m_udp, nullptr, 5, 2); // wait up to 5 seconds for established connection if (m_fd < 0) { return RESULT_ERR_GENERIC_IO; } - int ret; - if (m_udp) { - struct sockaddr_in bindAddress = address; - bindAddress.sin_addr.s_addr = INADDR_ANY; - ret = bind(m_fd, (struct sockaddr*)&bindAddress, sizeof(address)); - } else { - int value = 1; - ret = setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&value), sizeof(value)); - value = 1; - setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast(&value), sizeof(value)); - value = 3; // send keepalive after 3 seconds of silence - setsockopt(m_fd, IPPROTO_TCP, TCP_KEEPIDLE, reinterpret_cast(&value), sizeof(value)); - value = 2; // send keepalive in interval of 2 seconds - setsockopt(m_fd, IPPROTO_TCP, TCP_KEEPINTVL, reinterpret_cast(&value), sizeof(value)); - value = 2; // drop connection after 2 failed keep alive sends - setsockopt(m_fd, IPPROTO_TCP, TCP_KEEPCNT, reinterpret_cast(&value), sizeof(value)); - } - if (ret >= 0) { - ret = connect(m_fd, (struct sockaddr*)&address, sizeof(address)); - } - if (ret < 0) { - close(); - return RESULT_ERR_GENERIC_IO; - } if (!m_udp) { usleep(25000); // wait 25ms for potential initial garbage } diff --git a/src/lib/utils/tcpsocket.cpp b/src/lib/utils/tcpsocket.cpp index b24ab1cd..84a1e8ed 100755 --- a/src/lib/utils/tcpsocket.cpp +++ b/src/lib/utils/tcpsocket.cpp @@ -24,12 +24,14 @@ #include #include #include +#include +#include +#include #include #include #ifdef HAVE_PPOLL # include #endif -#include namespace ebusd { @@ -45,52 +47,73 @@ bool TCPSocket::isValid() { } -TCPSocket* TCPSocket::connect(const string& server, const uint16_t& port, int timeout) { - socketaddress address; - int ret; +int socketConnect(const char* server, uint16_t port, bool udp, socketaddress* storeAddress, int tcpConnectTimeout, +int tcpKeepAliveInterval) { + socketaddress localAddress; + socketaddress* address = storeAddress ? storeAddress : &localAddress; + memset(reinterpret_cast(address), 0, sizeof(*address)); - memset(reinterpret_cast(&address), 0, sizeof(address)); - - if (inet_addr(server.c_str()) == INADDR_NONE) { - struct hostent* he; - - he = gethostbyname(server.c_str()); + if (inet_aton(server, &address->sin_addr) == 0) { + struct hostent* he = gethostbyname(server); if (he == nullptr) { - return nullptr; + return -1; } - memcpy(&address.sin_addr, he->h_addr_list[0], he->h_length); - } else { - ret = inet_aton(server.c_str(), &address.sin_addr); - if (ret == 0) { - return nullptr; + memcpy(&address->sin_addr, he->h_addr_list[0], he->h_length); + } + address->sin_family = AF_INET; + address->sin_port = (in_port_t)htons(port); + + int sfd = socket(AF_INET, udp ? SOCK_DGRAM : SOCK_STREAM, 0); + if (sfd < 0) { + return -1; + } + int ret; + if (udp) { + struct sockaddr_in bindAddress = *address; + bindAddress.sin_addr.s_addr = INADDR_ANY; + ret = bind(sfd, (struct sockaddr*)&bindAddress, sizeof(bindAddress)); + if (ret >= 0) { + ret = ::connect(sfd, (struct sockaddr*)address, sizeof(*address)); + } + if (ret < 0) { + close(sfd); + return -1; } } - - address.sin_family = AF_INET; - address.sin_port = (in_port_t)htons(port); - - int sfd = socket(AF_INET, SOCK_STREAM, 0); - if (sfd < 0) { - return nullptr; + int value = 1; + ret = setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&value), sizeof(value)); + if (ret < 0) { + close(sfd); + return -1; + } + if (tcpKeepAliveInterval > 0) { + value = 1; + setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast(&value), sizeof(value)); + value = tcpKeepAliveInterval+1; // send keepalive after interval + 1 seconds of silence + setsockopt(sfd, IPPROTO_TCP, TCP_KEEPIDLE, reinterpret_cast(&value), sizeof(value)); + value = tcpKeepAliveInterval; // send keepalive in given interval + setsockopt(sfd, IPPROTO_TCP, TCP_KEEPINTVL, reinterpret_cast(&value), sizeof(value)); + value = 2; // drop connection after 2 failed keep alive sends + setsockopt(sfd, IPPROTO_TCP, TCP_KEEPCNT, reinterpret_cast(&value), sizeof(value)); } #ifndef HAVE_PPOLL #ifndef HAVE_PSELECT timeout = 0; #endif #endif - if (timeout > 0 && fcntl(sfd, F_SETFL, O_NONBLOCK) < 0) { // set non-blocking + if (tcpConnectTimeout > 0 && fcntl(sfd, F_SETFL, O_NONBLOCK) < 0) { // set non-blocking close(sfd); - return nullptr; + return -1; } - ret = ::connect(sfd, (struct sockaddr *) &address, sizeof(address)); + ret = ::connect(sfd, (struct sockaddr*)address, sizeof(*address)); if (ret != 0) { - if (ret < 0 && (timeout <= 0 || errno != EINPROGRESS)) { + if (ret < 0 && (tcpConnectTimeout <= 0 || errno != EINPROGRESS)) { close(sfd); - return nullptr; + return -1; } - if (timeout > 0) { + if (tcpConnectTimeout > 0) { struct timespec tdiff; - tdiff.tv_sec = timeout; + tdiff.tv_sec = tcpConnectTimeout; tdiff.tv_nsec = 0; #ifdef HAVE_PPOLL nfds_t nfds = 1; @@ -115,12 +138,22 @@ TCPSocket* TCPSocket::connect(const string& server, const uint16_t& port, int ti #endif if (ret == -1 || ret == 0) { close(sfd); - return nullptr; + return -1; + } + if (fcntl(sfd, F_SETFL, 0) < 0) { // set blocking again + close(sfd); + return -1; } } } - if (timeout > 0 && fcntl(sfd, F_SETFL, 0) < 0) { // set blocking again - close(sfd); + return sfd; +} + + +TCPSocket* TCPSocket::connect(const string& server, const uint16_t& port, int timeout) { + socketaddress address; + int sfd = socketConnect(server.c_str(), port, false, &address, timeout); + if (sfd < 0) { return nullptr; } TCPSocket* s = new TCPSocket(sfd, &address); @@ -137,21 +170,18 @@ int TCPServer::start() { } m_lfd = socket(AF_INET, SOCK_STREAM, 0); socketaddress address; - memset(&address, 0, sizeof(address)); address.sin_family = AF_INET; address.sin_port = (in_port_t)htons(m_port); - if (m_address.size() > 0) { - inet_pton(AF_INET, m_address.c_str(), &(address.sin_addr)); - } else { + if (!m_address.empty() && inet_pton(AF_INET, m_address.c_str(), &address.sin_addr) != 1) { address.sin_addr.s_addr = INADDR_ANY; } - int optval = 1; - setsockopt(m_lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + int value = 1; + setsockopt(m_lfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); - int result = bind(m_lfd, (struct sockaddr*) &address, sizeof(address)); + int result = bind(m_lfd, (struct sockaddr*)&address, sizeof(address)); if (result != 0) { return result; } @@ -169,10 +199,9 @@ TCPSocket* TCPServer::newSocket() { } socketaddress address; socklen_t len = sizeof(address); - memset(&address, 0, sizeof(address)); - int sfd = accept(m_lfd, (struct sockaddr*) &address, &len); + int sfd = accept(m_lfd, (struct sockaddr*)&address, &len); if (sfd < 0) { return nullptr; } diff --git a/src/lib/utils/tcpsocket.h b/src/lib/utils/tcpsocket.h index 06e7eab4..15a2c165 100755 --- a/src/lib/utils/tcpsocket.h +++ b/src/lib/utils/tcpsocket.h @@ -41,6 +41,20 @@ using std::string; #endif #endif +/** + * Connect a TCP or UDP socket. + * @param server the server name or ip address to connect to. + * @param port the port number. + * @param udp true for UDP, false for TCP. + * @param storeAddress optional pointer to where the socket address will be stored. + * @param tcpConnectTimeout the TCP connect timeout in seconds, or 0. + * @param tcpKeepAliveInterval optional interval in seconds for sending TCP keepalive. + * @return the connected socket file descriptor on success, or -1 on error. + */ +int socketConnect(const char* server, uint16_t port, bool udp, socketaddress* storeAddress = nullptr, +int tcpConnectTimeout = 0, int tcpKeepAliveInterval = 0); + + /** * Class for low level TCP socket operations (open, close, send, receive). */ diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index eff8a866..7c664e73 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -8,7 +8,7 @@ include_directories(intelhex) add_executable(ebusctl ${ebusctl_SOURCES}) add_executable(ebuspicloader ${ebuspicloader_SOURCES}) target_link_libraries(ebusctl utils ebus ${LIB_ARGP} ${ebusctl_LIBS}) -target_link_libraries(ebuspicloader ${LIB_ARGP}) +target_link_libraries(ebuspicloader utils ${LIB_ARGP} ${ebuspicloader_LIBS}) if(WITH_EBUSFEED) set(ebusfeed_SOURCES ebusfeed.cpp) diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index 806b8e1f..ca15aec4 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -8,6 +8,7 @@ ebusctl_SOURCES = ebusctl.cpp ebusctl_LDADD = ../lib/utils/libutils.a ebuspicloader_SOURCES = ebuspicloader.cpp intelhex/intelhexclass.cpp +ebusctl_LDADD = ../lib/utils/libutils.a if WITH_EBUSFEED bin_PROGRAMS += ebusfeed diff --git a/src/tools/ebuspicloader.cpp b/src/tools/ebuspicloader.cpp index 7f95f919..b4c15dba 100644 --- a/src/tools/ebuspicloader.cpp +++ b/src/tools/ebuspicloader.cpp @@ -37,7 +37,9 @@ #include #include #include "intelhex/intelhexclass.h" +#include "lib/utils/tcpsocket.h" +using ebusd::socketConnect; /** the version string of the program. */ const char *argp_program_version = "eBUS adapter PIC firmware loader"; @@ -661,32 +663,11 @@ int openSerial(std::string port) { int openNet(std::string host, uint16_t port) { // open network port - struct sockaddr_in address; - memset(reinterpret_cast(&address), 0, sizeof(address)); - if (inet_addr(host.c_str()) == INADDR_NONE) { - struct hostent* he; - he = gethostbyname(host.c_str()); - if (he == nullptr) { - std::cerr << "unable to resolve host " << host << std::endl; - return -1; - } - memcpy(&address.sin_addr, he->h_addr_list[0], he->h_length); - } else if (inet_aton(host.c_str(), &address.sin_addr) == 0) { - std::cerr << "unable to resolve IP " << host << std::endl; - return -1; - } - address.sin_family = AF_INET; - address.sin_port = (in_port_t)htons(port); - int fd = socket(AF_INET, SOCK_STREAM, 0); + int fd = socketConnect(host.c_str(), port, false, nullptr, 5); if (fd < 0) { std::cerr << "unable to open " << host << std::endl; return -1; } - if (connect(fd, (struct sockaddr *) &address, sizeof(address)) != 0) { - close(fd); - std::cerr << "unable to connect to " << host << std::endl; - return -1; - } fcntl(fd, F_SETFL, O_NONBLOCK); // set non-blocking return fd; }