123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- /*
- * 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 Henrik Ekblad <henrik.ekblad@mysensors.org>
- * Copyright (C) 2013-2018 Sensnology AB
- * 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 "EthernetClient.h"
- #include <cstdio>
- #include <sys/socket.h>
- #include <sys/ioctl.h>
- #include <linux/sockios.h>
- #include <netdb.h>
- #include <arpa/inet.h>
- #include <cstring>
- #include <unistd.h>
- #include <sys/time.h>
- #include <netinet/tcp.h>
- #include <errno.h>
- #include "log.h"
-
- EthernetClient::EthernetClient() : _sock(-1)
- {
- }
-
- EthernetClient::EthernetClient(int sock) : _sock(sock)
- {
- }
-
- int EthernetClient::connect(const char* host, uint16_t port)
- {
- struct addrinfo hints, *servinfo, *localinfo, *p;
- int rv;
- char s[INET6_ADDRSTRLEN];
- char port_str[6];
- bool use_bind = (_srcip != 0);
-
- close();
-
- 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;
- }
- if (use_bind) {
- if ((rv = getaddrinfo(_srcip.toString().c_str(), port_str, &hints, &localinfo)) != 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 ((_sock = socket(p->ai_family, p->ai_socktype,
- p->ai_protocol)) == -1) {
- logError("socket: %s\n", strerror(errno));
- continue;
- }
-
- if (use_bind) {
- if (::bind(_sock, localinfo->ai_addr, localinfo->ai_addrlen) == -1) {
- close();
- logError("bind: %s\n", strerror(errno));
- return -1;
- }
- }
-
- if (::connect(_sock, p->ai_addr, p->ai_addrlen) == -1) {
- close();
- logError("connect: %s\n", strerror(errno));
- continue;
- }
-
- break;
- }
-
- if (p == NULL) {
- logError("failed to connect\n");
- return -1;
- }
-
- 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
- if (use_bind) {
- freeaddrinfo(localinfo); // 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 bytes = 0;
-
- if (_sock == -1) {
- return 0;
- }
-
- while (size > 0) {
- int rc = send(_sock, buf + bytes, size, MSG_NOSIGNAL | MSG_DONTWAIT);
- if (rc == -1) {
- logError("send: %s\n", strerror(errno));
- close();
- 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, SIOCINQ, &count);
- }
-
- return count;
- }
-
- int EthernetClient::read()
- {
- uint8_t b;
- if ( recv(_sock, &b, 1, MSG_DONTWAIT) > 0 ) {
- // recv worked
- return b;
- } else {
- // No data available
- return -1;
- }
- }
-
- int EthernetClient::read(uint8_t *buf, size_t bytes)
- {
- return recv(_sock, buf, bytes, MSG_DONTWAIT);
- }
-
- int EthernetClient::peek()
- {
- uint8_t b;
-
- if (recv(_sock, &b, 1, MSG_PEEK | MSG_DONTWAIT) > 0) {
- return b;
- } else {
- return -1;
- }
- }
-
- void EthernetClient::flush()
- {
- int count = 0;
-
- if (_sock != -1) {
- while (true) {
- ioctl(_sock, SIOCOUTQ, &count);
- if (count == 0) {
- return;
- }
- usleep(1000);
- }
- }
- }
-
- 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
- do {
- uint8_t 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);
-
- // free up the socket descriptor
- ::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()
- {
- return status() == ETHERNETCLIENT_W5100_ESTABLISHED || available();
- }
-
- void EthernetClient::close()
- {
- if (_sock != -1) {
- ::close(_sock);
- _sock = -1;
- }
- }
-
- void EthernetClient::bind(IPAddress ip)
- {
- _srcip = ip;
- }
-
- 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;
- }
|