/* * The MySensors Arduino library handles the wireless radio link and protocol * between your home built sensors/actuators and HA controller of choice. * The sensors forms a self healing radio network with optional repeaters. Each * repeater and gateway builds a routing tables in EEPROM which keeps track of the * network topology allowing messages to be routed to nodes. * * Created by Marcelo Aquino * Copyright (C) 2016 Marcelo Aquino * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org * Support Forum: http://forum.mysensors.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. */ #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "EthernetClient.h" EthernetClient::EthernetClient() : _sock(-1) { } EthernetClient::EthernetClient(int sock) : _sock(sock) { } int EthernetClient::connect(const char* host, uint16_t port) { int sockfd; struct addrinfo hints, *servinfo, *p; int rv; char s[INET6_ADDRSTRLEN]; char port_str[6]; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; sprintf(port_str, "%hu", port); if ((rv = getaddrinfo(host, port_str, &hints, &servinfo)) != 0) { logError("getaddrinfo: %s\n", gai_strerror(rv)); return -1; } // loop through all the results and connect to the first we can for (p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { logError("socket: %s\n", strerror(errno)); continue; } if (::connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); logError("connect: %s\n", strerror(errno)); continue; } break; } if (p == NULL) { logError("failed to connect\n"); return -1; } _sock = sockfd; void *addr = &(((struct sockaddr_in*)p->ai_addr)->sin_addr); inet_ntop(p->ai_family, addr, s, sizeof s); logDebug("connected to %s\n", s); freeaddrinfo(servinfo); // all done with this structure return 1; } int EthernetClient::connect(IPAddress ip, uint16_t port) { return connect(ip.toString().c_str(), port); } size_t EthernetClient::write(uint8_t b) { return write(&b, 1); } size_t EthernetClient::write(const uint8_t *buf, size_t size) { int rc = 0; int bytes = 0; if (_sock == -1) { return 0; } while (size > 0) { rc = send(_sock, buf + bytes, size, MSG_NOSIGNAL | MSG_DONTWAIT); if (rc == -1) { logError("send: %s\n", strerror(errno)); close(_sock); _sock = -1; break; } bytes += rc; size -= rc; } return bytes; } size_t EthernetClient::write(const char *str) { if (str == NULL) { return 0; } return write((const uint8_t *)str, strlen(str)); } size_t EthernetClient::write(const char *buffer, size_t size) { return write((const uint8_t *)buffer, size); } int EthernetClient::available() { int count = 0; if (_sock != -1) { ioctl(_sock, FIONREAD, &count); return count; } return 0; } int EthernetClient::read() { uint8_t b; if ( recv(_sock, &b, 1, 0) > 0 ) { // recv worked return b; } else { // No data available return -1; } } int EthernetClient::read(uint8_t *buf, size_t size) { return recv(_sock, buf, size, 0); } int EthernetClient::peek() { uint8_t b; return recv(_sock, &b, 1, MSG_PEEK | MSG_DONTWAIT); } void EthernetClient::flush() { // There isn't much we can do here return; } void EthernetClient::stop() { if (_sock == -1) { return; } // attempt to close the connection gracefully (send a FIN to other side) shutdown(_sock, SHUT_RDWR); timeval startTime, curTime; gettimeofday(&startTime, NULL); // wait up to a second for the connection to close uint8_t s; do { s = status(); if (s == ETHERNETCLIENT_W5100_CLOSED) { break; // exit the loop } usleep(1000); gettimeofday(&curTime, NULL); } while (((curTime.tv_sec - startTime.tv_sec) * 1000000) + (curTime.tv_usec - startTime.tv_usec) < 1000000); // if it hasn't closed, close it forcefully if (s != ETHERNETCLIENT_W5100_CLOSED) { close(_sock); } _sock = -1; } uint8_t EthernetClient::status() { if (_sock == -1) { return ETHERNETCLIENT_W5100_CLOSED; } struct tcp_info tcp_info; int tcp_info_length = sizeof(tcp_info); if ( getsockopt( _sock, SOL_TCP, TCP_INFO, (void *)&tcp_info, (socklen_t *)&tcp_info_length ) == 0 ) { switch (tcp_info.tcpi_state) { case TCP_ESTABLISHED: return ETHERNETCLIENT_W5100_ESTABLISHED; case TCP_SYN_SENT: return ETHERNETCLIENT_W5100_SYNSENT; case TCP_SYN_RECV: return ETHERNETCLIENT_W5100_SYNRECV; case TCP_FIN_WAIT1: case TCP_FIN_WAIT2: return ETHERNETCLIENT_W5100_FIN_WAIT; case TCP_TIME_WAIT: return TCP_TIME_WAIT; case TCP_CLOSE: return ETHERNETCLIENT_W5100_CLOSED; case TCP_CLOSE_WAIT: return ETHERNETCLIENT_W5100_CLOSING; case TCP_LAST_ACK: return ETHERNETCLIENT_W5100_LAST_ACK; case TCP_LISTEN: return ETHERNETCLIENT_W5100_LISTEN; case TCP_CLOSING: return ETHERNETCLIENT_W5100_CLOSING; } } return ETHERNETCLIENT_W5100_CLOSED; } uint8_t EthernetClient::connected() { if (_sock == -1) { return 0; } if (peek() < 0) { if (errno == EAGAIN) { return 1; } return 0; } return 1; } int EthernetClient::getSocketNumber() { return _sock; } // the next function allows us to use the client returned by // EthernetServer::available() as the condition in an if-statement. EthernetClient::operator bool() { return _sock != -1; } bool EthernetClient::operator==(const EthernetClient& rhs) { return _sock == rhs._sock && _sock != -1 && rhs._sock != -1; }