/* PIP - Platform Independent Primitives Ethernet, UDP/TCP Broadcast/Multicast Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "piincludes_p.h" #include "piethernet.h" #include "piconfig.h" #include "pisysteminfo.h" #include "pipropertystorage.h" #ifdef QNX # include # include # include # include # include # include # include # include # include # include # include # include # ifdef BLACKBERRY # include # else # include # endif # define ip_mreqn ip_mreq # define imr_address imr_interface #else # ifdef WINDOWS # include # include # include # include # include # define ip_mreqn ip_mreq # define imr_address imr_interface # else # include # include # include # include # include # include # include # if !defined(ANDROID) && !defined(FREERTOS) # include # endif # ifdef FREERTOS # include # endif # endif #endif #include /** \class PIEthernet * \brief Ethernet device * \details * \section PIEthernet_sec0 Synopsis * %PIEthernet designed to work with IPv4 network via two protocols: * UDP and TCP. This class allow you send and receive packets to/from * another computer through network. Also it supports broadcast and * multicast extensions. * * \section PIEthernet_sec1 IPv4 * * * \section PIEthernet_sec2 UDP * User Datagram Protocol * * \section PIEthernet_sec3 TCP * Transmission Control Protocol * * */ #ifndef WINDOWS PIString getSockAddr(sockaddr * s) { return s == 0 ? PIString() : PIStringAscii(inet_ntoa(((sockaddr_in*)s)->sin_addr)); } #endif PIEthernet::Address::Address(uint _ip, ushort _port) { set(_ip, _port); } PIEthernet::Address::Address(const PIString & ip_port) { set(ip_port); } PIEthernet::Address::Address(const PIString & _ip, ushort _port) { set(_ip, _port); } PIString PIEthernet::Address::ipString() const { PIString ret = PIString::fromNumber(ip_b[0]); ret << "." << PIString::fromNumber(ip_b[1]); ret << "." << PIString::fromNumber(ip_b[2]); ret << "." << PIString::fromNumber(ip_b[3]); return ret; } PIString PIEthernet::Address::toString() const { return ipString() + ":" + PIString::fromNumber(port_); } void PIEthernet::Address::setIP(uint _ip) { ip_ = _ip; } void PIEthernet::Address::setIP(const PIString & _ip) { initIP(_ip); } void PIEthernet::Address::setPort(ushort _port) { port_ = _port; } void PIEthernet::Address::set(const PIString & ip_port) { PIString _ip; int p(0); splitIPPort(ip_port, &_ip, &p); port_ = p; initIP(_ip); } void PIEthernet::Address::set(const PIString & _ip, ushort _port) { initIP(_ip); port_ = _port; } void PIEthernet::Address::set(uint _ip, ushort _port) { ip_ = _ip; port_ = _port; } void PIEthernet::Address::clear() { ip_ = 0; port_ = 0; } bool PIEthernet::Address::isNull() const { return (ip_ == 0) && (port_ == 0); } PIEthernet::Address PIEthernet::Address::resolve(const PIString & host_port) { PIString host; int port(0); splitIPPort(host_port, &host, &port); return resolve(host, port); } PIEthernet::Address PIEthernet::Address::resolve(const PIString & host, ushort port) { Address ret(0, port); hostent * he = gethostbyname(host.dataAscii()); if (!he) return ret; if (he->h_addr_list[0]) ret.setIP(*((uint*)(he->h_addr_list[0]))); return ret; } void PIEthernet::Address::splitIPPort(const PIString & ipp, PIString * _ip, int * _port) { //piCout << "parse" << ipp; int sp = ipp.findLast(":"); if (_ip != 0) *_ip = (sp >= 0 ? ipp.left(sp) : ipp); if (_port != 0 && sp >= 0) *_port = ipp.right(ipp.length() - ipp.find(":") - 1).toInt(); } void PIEthernet::Address::initIP(const PIString & _ip) { ip_ = inet_addr(_ip.dataAscii()); } REGISTER_DEVICE(PIEthernet) PRIVATE_DEFINITION_START(PIEthernet) sockaddr_in addr_; sockaddr_in saddr_; sockaddr_in raddr_; PRIVATE_DEFINITION_END(PIEthernet) PIEthernet::PIEthernet(): PIIODevice("", ReadWrite) { construct(); setType(UDP); setParameters(PIEthernet::ReuseAddress | PIEthernet::MulticastLoop | PIEthernet::KeepConnection); } PIEthernet::PIEthernet(PIEthernet::Type type_, const PIString & ip_port, const PIFlags params_): PIIODevice(ip_port, ReadWrite) { construct(); addr_r.set(ip_port); setType(type_); setParameters(params_); if (type_ != UDP) init(); } PIEthernet::PIEthernet(int sock_, PIString ip_port): PIIODevice("", ReadWrite) { construct(); addr_s.set(ip_port); sock = sock_; opened_ = connected_ = true; init(); setParameters(PIEthernet::ReuseAddress | PIEthernet::MulticastLoop); setType(TCP_Client, false); setPath(ip_port); //piCoutObj << "new tcp client" << sock_; } PIEthernet::~PIEthernet() { //piCout << "~PIEthernet" << uint(this); stop(); close(); //piCoutObj << "~PIEthernet done"; } void PIEthernet::construct() { //piCout << " PIEthernet" << uint(this); setOption(BlockingWrite); connected_ = connecting_ = listen_threaded = server_bounded = false; sock = sock_s = -1; setReadTimeout(10000.); setWriteTimeout(10000.); setTTL(64); setMulticastTTL(1); server_thread_.setData(this); server_thread_.setName("__S__server_thread"); #ifdef FREERTOS setThreadedReadBufferSize(512); #else setThreadedReadBufferSize(65536); #endif setPriority(piHigh); } bool PIEthernet::init() { if (isOpened()) return true; //cout << "init " << type_ << endl; if (sock_s == sock) sock_s = -1; closeSocket(sock); closeSocket(sock_s); int st = 0, pr = 0; if (type() == UDP) { st = SOCK_DGRAM; pr = IPPROTO_UDP; } else { st = SOCK_STREAM; pr = IPPROTO_TCP; } PIFlags params = parameters(); sock = ::socket(AF_INET, st, pr); if (params[SeparateSockets]) sock_s = ::socket(AF_INET, st, pr); else sock_s = sock; if (sock == -1 || sock_s == -1) { piCoutObj << "Can`t create socket," << ethErrorString(); return false; } if (params[PIEthernet::ReuseAddress]) ethSetsockoptBool(sock, SOL_SOCKET, SO_REUSEADDR); if (params[PIEthernet::Broadcast]) ethSetsockoptBool(sock, SOL_SOCKET, SO_BROADCAST); applyTimeouts(); applyOptInt(IPPROTO_IP, IP_TTL, TTL()); // piCoutObj << "inited" << path(); return true; } PIString PIEthernet::macFromBytes(const PIByteArray & mac) { PIString r; for (int i = 0; i < mac.size_s(); ++i) { r += PIString::fromNumber(mac[i], 16).expandLeftTo(2, '0'); if (i < mac.size_s() - 1) r += ":"; } return r; } PIByteArray PIEthernet::macToBytes(const PIString & mac) { PIByteArray r; PIStringList sl = mac.split(PIStringAscii(":")); piForeachC (PIString & i, sl) r << uchar(i.toInt(16)); return r; } PIString PIEthernet::applyMask(const PIString & ip, const PIString & mask) { struct in_addr ia; ia.s_addr = inet_addr(ip.dataAscii()) & inet_addr(mask.dataAscii()); return PIStringAscii(inet_ntoa(ia)); } PIEthernet::Address PIEthernet::applyMask(const PIEthernet::Address & ip, const PIEthernet::Address & mask) { Address ret(ip); ret.ip_ &= mask.ip_; return ret; } PIString PIEthernet::getBroadcast(const PIString & ip, const PIString & mask) { struct in_addr ia; ia.s_addr = inet_addr(ip.dataAscii()) | ~inet_addr(mask.dataAscii()); return PIStringAscii(inet_ntoa(ia)); } PIEthernet::Address PIEthernet::getBroadcast(const PIEthernet::Address & ip, const PIEthernet::Address & mask) { Address ret(ip); ret.ip_ |= ~mask.ip_; return ret; } bool PIEthernet::openDevice() { if (connected_) return true; init(); if (sock == -1 || path().isEmpty()) return false; addr_r.set(path()); if (type() == TCP_Client) connecting_ = true; if (type() != UDP || mode() == PIIODevice::WriteOnly) return true; memset(&PRIVATE->addr_, 0, sizeof(PRIVATE->addr_)); PRIVATE->addr_.sin_family = AF_INET; PRIVATE->addr_.sin_port = htons(addr_r.port()); PIFlags params = parameters(); if (params[PIEthernet::Broadcast]) PRIVATE->addr_.sin_addr.s_addr = INADDR_ANY; else PRIVATE->addr_.sin_addr.s_addr = addr_r.ip(); #ifdef QNX PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_); #endif //piCout << "bind to" << (params[PIEthernet::Broadcast] ? "255.255.255.255" : ip_) << ":" << port_ << " ..."; int tries = 0; while ((bind(sock, (sockaddr * )&PRIVATE->addr_, sizeof(PRIVATE->addr_)) == -1) && (tries < 2)) { init(); tries++; } if (tries == 2) { piCoutObj << "Can`t bind to" << addr_r << "," << ethErrorString(); return false; } opened_ = true; while (!mcast_queue.isEmpty()) joinMulticastGroup(mcast_queue.dequeue()); applyTimeouts(); applyOptInt(IPPROTO_IP, IP_TTL, TTL()); addr_lr.clear(); return true; } bool PIEthernet::closeDevice() { //cout << "close\n"; if (server_thread_.isRunning()) { server_thread_.stop(); if (!server_thread_.waitForFinish(100)) server_thread_.terminate(); } if (sock_s == sock) sock_s = -1; closeSocket(sock); closeSocket(sock_s); while (!clients_.isEmpty()) delete clients_.back(); bool ned = connected_; connected_ = connecting_ = false; if (ned) disconnected(false); return true; } void PIEthernet::closeSocket(int & sd) { if (sd != -1) ethClosesocket(sd, type() != PIEthernet::UDP); sd = -1; } void PIEthernet::applyTimeouts() { if (sock < 0) return; double rtm = readTimeout(), wtm = writeTimeout(); applyTimeout(sock, SO_RCVTIMEO, rtm); applyTimeout(sock, SO_SNDTIMEO, wtm); if (sock_s != sock && sock_s != -1) { applyTimeout(sock_s, SO_RCVTIMEO, rtm); applyTimeout(sock_s, SO_SNDTIMEO, wtm); } } void PIEthernet::applyTimeout(int fd, int opt, double ms) { if (fd == 0) return; //piCoutObj << "setReadIsBlocking" << yes; #ifdef WINDOWS DWORD _tm = ms; #else double s = ms / 1000.; timeval _tm; _tm.tv_sec = piFloord(s); s -= _tm.tv_sec; _tm.tv_usec = s * 1000000.; #endif ethSetsockopt(fd, SOL_SOCKET, opt, &_tm, sizeof(_tm)); } void PIEthernet::applyOptInt(int level, int opt, int val) { if (sock < 0) return; ethSetsockoptInt(sock, level, opt, val); if (sock_s != sock && sock_s != -1) ethSetsockoptInt(sock_s, level, opt, val); } void PIEthernet::setParameter(PIEthernet::Parameters parameter, bool on) { PIFlags cp = (PIFlags)(property("parameters").toInt()); cp.setFlag(parameter, on); setParameters(cp); } bool PIEthernet::joinMulticastGroup(const PIString & group) { if (sock == -1) init(); if (sock == -1) return false; if (type() != UDP) { piCoutObj << "Only UDP sockets can join multicast groups"; return false; } if (isClosed()) { if (mcast_queue.contains(group)) return false; mcast_queue.enqueue(group); if (!mcast_groups.contains(group)) mcast_groups << group; return true; } PIFlags params = parameters(); addr_r.set(path()); #ifndef FREERTOS struct ip_mreqn mreq; #else struct ip_mreq mreq; #endif memset(&mreq, 0, sizeof(mreq)); #ifdef LINUX //mreq.imr_address.s_addr = INADDR_ANY; /*PIEthernet::InterfaceList il = interfaces(); const PIEthernet::Interface * ci = il.getByAddress(addr_r.ipString()); if (ci != 0) mreq.imr_ifindex = ci->index;*/ #endif if (params[PIEthernet::Broadcast]) #ifndef FREERTOS mreq.imr_address.s_addr = INADDR_ANY; #else mreq.imr_interface.s_addr = INADDR_ANY; #endif else #ifndef FREERTOS mreq.imr_address.s_addr = addr_r.ip(); #else mreq.imr_interface.s_addr = addr_r.ip(); #endif //piCout << "join group" << group << "ip" << ip_ << "with index" << mreq.imr_ifindex << "socket" << sock; mreq.imr_multiaddr.s_addr = inet_addr(group.dataAscii()); if (ethSetsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0) { piCoutObj << "Can`t join multicast group" << group << "," << ethErrorString(); return false; } if (params[PIEthernet::MulticastLoop]) ethSetsockoptInt(sock, IPPROTO_IP, IP_MULTICAST_LOOP); applyOptInt(IPPROTO_IP, IP_MULTICAST_TTL, multicastTTL()); if (!mcast_groups.contains(group)) mcast_groups << group; return true; } bool PIEthernet::leaveMulticastGroup(const PIString & group) { if (sock == -1) init(); if (sock == -1) return false; if (type() != UDP) { piCoutObj << "Only UDP sockets can leave multicast groups"; return false; } PIFlags params = parameters(); addr_r.set(path()); #ifndef FREERTOS struct ip_mreqn mreq; #else struct ip_mreq mreq; #endif memset(&mreq, 0, sizeof(mreq)); if (params[PIEthernet::Broadcast]) #ifndef FREERTOS mreq.imr_address.s_addr = INADDR_ANY; #else mreq.imr_interface.s_addr = INADDR_ANY; #endif else #ifndef FREERTOS mreq.imr_address.s_addr = addr_r.ip(); #else mreq.imr_interface.s_addr = addr_r.ip(); #endif mreq.imr_multiaddr.s_addr = inet_addr(group.dataAscii()); if (ethSetsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { piCoutObj << "Can`t leave multicast group" << group << "," << ethErrorString(); return false; } mcast_groups.removeAll(group); return true; } bool PIEthernet::connect(bool threaded) { if (threaded) { connecting_ = true; return true; } if (sock == -1) init(); if (sock == -1) return false; memset(&PRIVATE->addr_, 0, sizeof(PRIVATE->addr_)); addr_r.set(path()); PRIVATE->addr_.sin_port = htons(addr_r.port()); PRIVATE->addr_.sin_addr.s_addr = addr_r.ip(); PRIVATE->addr_.sin_family = AF_INET; #ifdef QNX PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_); #endif connected_ = (::connect(sock, (sockaddr * )&PRIVATE->addr_, sizeof(PRIVATE->addr_)) == 0); if (!connected_) { piCoutObj << "Can`t connect to" << addr_r << "," << ethErrorString(); } opened_ = connected_; if (connected_) { connecting_ = false; connected(); } return connected_; } bool PIEthernet::listen(bool threaded) { if (sock == -1) init(); if (sock == -1) return false; if (threaded) { if (server_thread_.isRunning()) { if (!server_bounded) return true; server_thread_.stop(); if (!server_thread_.waitForFinish(100)) server_thread_.terminate(); } listen_threaded = true; server_bounded = false; server_thread_.start(server_func); return true; } listen_threaded = server_bounded = false; addr_r.set(path()); memset(&PRIVATE->addr_, 0, sizeof(PRIVATE->addr_)); PRIVATE->addr_.sin_port = htons(addr_r.port()); PRIVATE->addr_.sin_addr.s_addr = addr_r.ip(); PRIVATE->addr_.sin_family = AF_INET; #ifdef QNX PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_); #endif opened_ = false; int tries = 0; while ((bind(sock, (sockaddr * )&PRIVATE->addr_, sizeof(PRIVATE->addr_)) == -1) && (tries < 2)) { init(); tries++; } if (tries == 2) { piCoutObj << "Can`t bind to" << addr_r << "," << ethErrorString(); return false; } if (::listen(sock, 64) == -1) { piCoutObj << "Can`t listen on"<< addr_r << "," << ethErrorString(); return false; } opened_ = server_bounded = true; //piCoutObj << "listen on " << ip_ << ":" << port_; server_thread_.start(server_func); return true; } bool PIEthernet::listen(const PIEthernet::Address & addr, bool threaded) { setReadAddress(addr); return listen(threaded); } bool PIEthernet::send(const void * data, int size, bool threaded) { if (threaded) { writeThreaded(data, size); return true; } return (write(data, size) == size); } bool PIEthernet::send(const PIEthernet::Address & addr, const void * data, int size, bool threaded) { addr_s = addr; if (threaded) { writeThreaded(data, size); return true; } Address pa = addr_s; addr_s = addr; int wr = write(data, size); addr_s = pa; return (wr == size); } bool PIEthernet::send(const PIByteArray & data, bool threaded) { if (threaded) { writeThreaded(data); return true; } return (write(data) == data.size_s()); } bool PIEthernet::send(const PIEthernet::Address & addr, const PIByteArray & data, bool threaded) { if (threaded) { writeThreaded(data); return true; } Address pa = addr_s; addr_s = addr; int wr = write(data); addr_s = pa; return (wr == data.size_s()); } int PIEthernet::readDevice(void * read_to, int max_size) { //piCout << "read" << sock; if (sock == -1) init(); if (sock == -1 || read_to == 0) return -1; int rs = 0, s = 0, lerr = 0; sockaddr_in client_addr; socklen_t slen = sizeof(client_addr); // piCoutObj << "read from " << ip_ << ":" << port_; switch (type()) { case TCP_SingleTCP: ::listen(sock, 64); s = accept(sock, (sockaddr * )&client_addr, &slen); if (s == -1) { //piCoutObj << "Can`t accept new connection, " << ethErrorString(); msleep(PIP_MIN_MSLEEP); return -1; } rs = ethRecv(s, read_to, max_size); closeSocket(s); return rs; case TCP_Client: if (connecting_) { #ifdef ANDROID /*if (sock_s == sock) sock_s = -1; closeSocket(sock); closeSocket(sock_s); init(); qDebug() << "init() in read thread";*/ #endif addr_r.set(path()); memset(&PRIVATE->addr_, 0, sizeof(PRIVATE->addr_)); PRIVATE->addr_.sin_port = htons(addr_r.port()); PRIVATE->addr_.sin_addr.s_addr = addr_r.ip(); PRIVATE->addr_.sin_family = AF_INET; #ifdef QNX PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_); #endif //piCoutObj << "connect to " << ip_ << ":" << port_ << "..."; connected_ = (::connect(sock, (sockaddr * )&(PRIVATE->addr_), sizeof(PRIVATE->addr_)) == 0); //piCoutObj << "connect to " << ip_ << ":" << port_ << connected_; if (!connected_) piCoutObj << "Can`t connect to" << addr_r << "," << ethErrorString(); opened_ = connected_; if (connected_) { connecting_ = false; connected(); } else piMSleep(10); //piCout << "connected to" << path(); } if (!connected_) return -1; errorClear(); rs = ethRecv(sock, read_to, max_size); //piCoutObj << "readed" << rs; if (rs <= 0) { lerr = ethErrorCore(); //piCoutObj << "readed error" << lerr << errorString().data() << parameters()[DisonnectOnTimeout]; #ifdef WINDOWS if ((lerr == WSAEWOULDBLOCK || lerr == WSAETIMEDOUT) && !parameters()[DisonnectOnTimeout]) { #elif defined(ANDROID) if ((lerr == EWOULDBLOCK || lerr == EAGAIN || lerr == EINTR) && !parameters()[DisonnectOnTimeout]) { #else if ((lerr == EWOULDBLOCK || lerr == EAGAIN) && !parameters()[DisonnectOnTimeout]) { #endif //piCoutObj << errorString(); return -1; } if (connected_) { piCoutObj << "Disconnect on read," << ethErrorString(); opened_ = connected_ = false; init(); disconnected(rs < 0); } if (parameters()[KeepConnection]) connect(); //piCoutObj << "eth" << ip_ << "disconnected"; } if (rs > 0) received(read_to, rs); return rs; case UDP: memset(&PRIVATE->raddr_, 0, sizeof(PRIVATE->raddr_)); rs = ethRecvfrom(sock, read_to, max_size, 0, (sockaddr*)&PRIVATE->raddr_); if (rs > 0) { addr_lr.set(uint(PRIVATE->raddr_.sin_addr.s_addr), ntohs(PRIVATE->raddr_.sin_port)); //piCoutObj << "read from" << ip_r << ":" << port_r << rs << "bytes"; received(read_to, rs); } //else piCoutObj << "read returt" << rs << ", error" << ethErrorString(); return rs; default: break; } return -1; } int PIEthernet::writeDevice(const void * data, int max_size) { if (sock == -1) init(); if (sock == -1 || !isWriteable()) { //piCoutObj << "Can`t send to uninitialized socket"; return -1; } //piCoutObj << "sending to " << ip_s << ":" << port_s << " " << max_size << " bytes"; int ret = 0; switch (type()) { case TCP_SingleTCP: memset(&PRIVATE->addr_, 0, sizeof(PRIVATE->addr_)); PRIVATE->addr_.sin_port = htons(addr_s.port()); PRIVATE->addr_.sin_addr.s_addr = addr_s.ip(); PRIVATE->addr_.sin_family = AF_INET; #ifdef QNX PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_); #endif //piCoutObj << "connect SingleTCP" << ip_s << ":" << port_s << "..."; if (::connect(sock, (sockaddr * )&PRIVATE->addr_, sizeof(PRIVATE->addr_)) != 0) { //piCoutObj << "Can`t connect to " << ip_s << ":" << port_s << ", " << ethErrorString(); msleep(PIP_MIN_MSLEEP); return -1; } //piCoutObj << "ok, write SingleTCP" << int(data) << max_size << "bytes ..."; ret = ::send(sock, (const char *)data, max_size, 0); //piCoutObj << "ok, ret" << ret; closeSocket(sock); init(); return ret; case UDP: PRIVATE->saddr_.sin_port = htons(addr_s.port()); PRIVATE->saddr_.sin_addr.s_addr = addr_s.ip(); PRIVATE->saddr_.sin_family = AF_INET; //piCoutObj << "write to" << ip_s << ":" << port_s << "socket" << sock_s << max_size << "bytes ..."; return ethSendto(sock_s, data, max_size, #ifndef WINDOWS isOptionSet(BlockingWrite) ? 0 : MSG_DONTWAIT #else 0 #endif , (sockaddr * )&PRIVATE->saddr_, sizeof(PRIVATE->saddr_)); //piCout << "[PIEth] write to" << ip_s << ":" << port_s << "ok"; case TCP_Client: if (connecting_) { memset(&PRIVATE->addr_, 0, sizeof(PRIVATE->addr_)); addr_r.set(path()); PRIVATE->addr_.sin_port = htons(addr_r.port()); PRIVATE->addr_.sin_addr.s_addr = addr_r.ip(); PRIVATE->addr_.sin_family = AF_INET; #ifdef QNX PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_); #endif //piCoutObj << "connect to " << ip << ":" << port_; connected_ = (::connect(sock, (sockaddr * )&PRIVATE->addr_, sizeof(PRIVATE->addr_)) == 0); if (!connected_) piCoutObj << "Can`t connect to" << addr_r << "," << ethErrorString(); opened_ = connected_; if (connected_) { connecting_ = false; connected(); } } if (!connected_) return -1; ret = ::send(sock, (const char *)data, max_size, 0); if (ret < 0) { piCoutObj << "Disconnect on write," << ethErrorString(); opened_ = connected_ = false; init(); disconnected(true); } return ret; default: break; } return -1; } PIIODevice::DeviceInfoFlags PIEthernet::deviceInfoFlags() const { switch (type()) { case UDP: return 0; case TCP_Client: case TCP_Server: case TCP_SingleTCP: return Sequential | Reliable; default: break; } return 0; } void PIEthernet::clientDeleted(PIObject * o) { clients_mutex.lock(); clients_.removeOne((PIEthernet*)o); clients_mutex.unlock(); } void PIEthernet::server_func(void * eth) { PIEthernet * ce = (PIEthernet * )eth; if (ce->listen_threaded) { if (!ce->server_bounded) { if (!ce->listen(false)) { ce->listen_threaded = true; piMSleep(100); return; } } } sockaddr_in client_addr; socklen_t slen = sizeof(client_addr); int s = accept(ce->sock, (sockaddr * )&client_addr, &slen); if (s == -1) { int lerr = ethErrorCore(); #ifdef WINDOWS if (lerr == WSAETIMEDOUT) { #elif defined(ANDROID) if ((lerr == EAGAIN || lerr == EINTR)) { #else if (lerr == EAGAIN) { #endif piMSleep(10); return; } if (ce->debug()) piCout << "[PIEthernet] Can`t accept new connection," << ethErrorString(); piMSleep(10); return; } PIString ip = PIStringAscii(inet_ntoa(client_addr.sin_addr)); ip += ":" + PIString::fromNumber(htons(client_addr.sin_port)); PIEthernet * e = new PIEthernet(s, ip); ce->clients_mutex.lock(); CONNECTU(e, deleted, ce, clientDeleted) ce->clients_ << e; ce->clients_mutex.unlock(); ce->newConnection(e); //cout << "connected " << ip << endl; } bool PIEthernet::configureDevice(const void * e_main, const void * e_parent) { PIConfig::Entry * em = (PIConfig::Entry * )e_main; PIConfig::Entry * ep = (PIConfig::Entry * )e_parent; setReadIP(readDeviceSetting("ip", readIP(), em, ep)); setReadPort(readDeviceSetting("port", readPort(), em, ep)); setParameter(PIEthernet::Broadcast, readDeviceSetting("broadcast", isParameterSet(PIEthernet::Broadcast), em, ep)); setParameter(PIEthernet::ReuseAddress, readDeviceSetting("reuseAddress", isParameterSet(PIEthernet::ReuseAddress), em, ep)); return true; } void PIEthernet::propertyChanged(const PIString & name) { if (name.endsWith("Timeout")) applyTimeouts(); if (name == "TTL") applyOptInt(IPPROTO_IP, IP_TTL, TTL()); if (name == "MulticastTTL") applyOptInt(IPPROTO_IP, IP_MULTICAST_TTL, multicastTTL()); } PIString PIEthernet::constructFullPathDevice() const { PIString ret; ret << (type() == PIEthernet::UDP ? "UDP" : "TCP") << ":" << readIP() << ":" << readPort(); if (type() == PIEthernet::UDP) { ret << ":" << sendIP() << ":" << sendPort(); piForeachC (PIString & m, multicastGroups()) ret << ":mcast:" << m; } return ret; } void PIEthernet::configureFromFullPathDevice(const PIString & full_path) { PIStringList pl = full_path.split(":"); bool mcast = false; for (int i = 0; i < pl.size_s(); ++i) { PIString p(pl[i]); switch (i) { case 0: p = p.toLowerCase(); if (p == "udp") setType(UDP); if (p == "tcp") setType(TCP_Client); break; case 1: setReadIP(p); setSendIP(p); break; case 2: setReadPort(p.toInt()); setSendPort(p.toInt()); break; case 3: setSendIP(p); break; case 4: setSendPort(p.toInt()); break; } if (i <= 4) continue; if (i % 2 == 1) {if (p.toLowerCase() == "mcast") mcast = true;} else {if (mcast) {joinMulticastGroup(p); mcast = false;}} } } PIPropertyStorage PIEthernet::constructVariantDevice() const { PIPropertyStorage ret; PIVariantTypes::Enum e; e << "UDP" << "TCP"; if (type() == PIEthernet::UDP) e.selectValue(0); else e.selectValue(1); ret.addProperty("protocol", e); ret.addProperty("read IP", readIP()); ret.addProperty("read port", readPort()); ret.addProperty("send IP", sendIP()); ret.addProperty("send port", sendPort()); ret.addProperty("multicast", multicastGroups()); return ret; } void PIEthernet::configureFromVariantDevice(const PIPropertyStorage & d) { setType(d.propertyValueByName("protocol").toEnum().selectedValue() == 0 ? UDP : TCP_Client); setReadIP(d.propertyValueByName("read IP").toString()); setReadPort(d.propertyValueByName("read port").toInt()); setSendIP(d.propertyValueByName("send IP").toString()); setSendPort(d.propertyValueByName("send port").toInt()); PIStringList mcgl = d.propertyValueByName("multicast").toStringList(); piForeachC (PIString & g, mcgl) { joinMulticastGroup(g); } } PIEthernet::InterfaceList PIEthernet::interfaces() { PIEthernet::InterfaceList il; Interface ci; ci.index = -1; ci.mtu = 1500; #ifdef WINDOWS PIP_ADAPTER_INFO pAdapterInfo, pAdapter = 0; int ret = 0; ulong ulOutBufLen = sizeof(IP_ADAPTER_INFO); pAdapterInfo = (IP_ADAPTER_INFO * ) HeapAlloc(GetProcessHeap(), 0, (sizeof (IP_ADAPTER_INFO))); if (pAdapterInfo == 0) { piCout << "[PIEthernet] Error allocating memory needed to call GetAdaptersinfo"; return il; } if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) { HeapFree(GetProcessHeap(), 0, (pAdapterInfo)); pAdapterInfo = (IP_ADAPTER_INFO *) HeapAlloc(GetProcessHeap(), 0, (ulOutBufLen)); if (pAdapterInfo == 0) { piCout << "[PIEthernet] Error allocating memory needed to call GetAdaptersinfo"; return il; } } if ((ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) { pAdapter = pAdapterInfo; while (pAdapter) { ci.name = PIString(pAdapter->AdapterName); ci.index = pAdapter->Index; ci.mac = macFromBytes(PIByteArray(pAdapter->Address, pAdapter->AddressLength)); ci.flags = PIEthernet::ifActive | PIEthernet::ifRunning; if (pAdapter->Type == MIB_IF_TYPE_PPP) ci.flags |= PIEthernet::ifPTP; if (pAdapter->Type == MIB_IF_TYPE_LOOPBACK) ci.flags |= PIEthernet::ifLoopback; ci.broadcast.clear(); ci.ptp.clear(); IP_ADDR_STRING * as = &(pAdapter->IpAddressList); while (as) { // piCout << "[pAdapter]" << ci.name << PIString(as->IpAddress.String); ci.address = PIString(as->IpAddress.String); ci.netmask = PIString(as->IpMask.String); if (ci.address == "0.0.0.0") { as = as->Next; continue; } il << ci; as = as->Next; } pAdapter = pAdapter->Next; } } else piCout << "[PIEthernet] GetAdaptersInfo failed with error:" << ret; if (pAdapterInfo) HeapFree(GetProcessHeap(), 0, (pAdapterInfo)); #else #ifdef FREERTOS #else # ifdef ANDROID struct ifconf ifc; int s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP); ifc.ifc_len = 256; ifc.ifc_buf = new char[ifc.ifc_len]; if (ioctl(s, SIOCGIFCONF, &ifc) < 0) { piCout << "[PIEthernet] Can`t get interfaces:" << errorString(); delete[] ifc.ifc_buf; return il; } int icnt = ifc.ifc_len / sizeof(ifreq); PIStringList inl; struct ifreq ir; for (int i = 0; i < icnt; ++i) { ci.flags = 0; PIString in = PIStringAscii(ifc.ifc_req[i].ifr_name); if (in.isEmpty()) continue; ci.name = in; strcpy(ir.ifr_name, in.dataAscii()); if (ioctl(s, SIOCGIFHWADDR, &ir) == 0) ci.mac = macFromBytes(PIByteArray(ir.ifr_hwaddr.sa_data, 6)); if (ioctl(s, SIOCGIFADDR, &ir) >= 0) ci.address = getSockAddr(&ir.ifr_addr); if (ioctl(s, SIOCGIFNETMASK, &ir) >= 0) ci.netmask = getSockAddr(&ir.ifr_addr); ioctl(s, SIOCGIFMTU, &ci.mtu); if (ci.address == "127.0.0.1") ci.flags |= PIEthernet::ifLoopback; il << ci; } delete ifc.ifc_buf; # else struct ifaddrs * ret, * cif = 0; int s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (getifaddrs(&ret) == 0) { cif = ret; while (cif != 0) { if (cif->ifa_addr == 0) { cif = cif->ifa_next; continue; } if (cif->ifa_addr->sa_family != AF_INET) { cif = cif->ifa_next; continue; } ci.name = PIString(cif->ifa_name); ci.address = getSockAddr(cif->ifa_addr); ci.netmask = getSockAddr(cif->ifa_netmask); ci.mac.clear(); # ifdef QNX # ifndef BLACKBERRY int fd = ::open((PIString("/dev/io-net/") + ci.name).dataAscii(), O_RDONLY); if (fd != 0) { nic_config_t nic; devctl(fd, DCMD_IO_NET_GET_CONFIG, &nic, sizeof(nic), 0); ::close(fd); ci.mac = macFromBytes(PIByteArray(nic.permanent_address, 6)); } # endif # else # ifdef MAC_OS PIString req = PISystemInfo::instance()->ifconfigPath + " " + ci.name + " | grep ether"; FILE * fp = popen(req.dataAscii(), "r"); if (fp != 0) { char in[256]; if (fgets(in, 256, fp) != 0) { req = PIString(in).trim(); ci.mac = req.cutLeft(req.find(" ") + 1).trim().toUpperCase(); } pclose(fp); } # else if (s != -1) { struct ifreq ir; strcpy(ir.ifr_name, cif->ifa_name); if (ioctl(s, SIOCGIFHWADDR, &ir) == 0) { ci.mac = macFromBytes(PIByteArray(ir.ifr_hwaddr.sa_data, 6)); ci.mtu = ir.ifr_mtu; } } # endif # endif ci.flags = 0; if (cif->ifa_flags & IFF_UP) ci.flags |= PIEthernet::ifActive; if (cif->ifa_flags & IFF_RUNNING) ci.flags |= PIEthernet::ifRunning; if (cif->ifa_flags & IFF_BROADCAST) ci.flags |= PIEthernet::ifBroadcast; if (cif->ifa_flags & IFF_MULTICAST) ci.flags |= PIEthernet::ifMulticast; if (cif->ifa_flags & IFF_LOOPBACK) ci.flags |= PIEthernet::ifLoopback; if (cif->ifa_flags & IFF_POINTOPOINT) ci.flags |= PIEthernet::ifPTP; ci.broadcast.clear(); ci.ptp.clear(); if (ci.flags[PIEthernet::ifBroadcast]) ci.broadcast = getSockAddr(cif->ifa_broadaddr); if (ci.flags[PIEthernet::ifPTP]) ci.ptp = getSockAddr(cif->ifa_dstaddr); ci.index = if_nametoindex(cif->ifa_name); il << ci; cif = cif->ifa_next; } freeifaddrs(ret); } else piCout << "[PIEthernet] Can`t get interfaces:" << errorString(); if (s != -1) ::close(s); # endif # endif #endif return il; } PIEthernet::Address PIEthernet::interfaceAddress(const PIString & interface_) { #if defined(WINDOWS) || defined(FREERTOS) piCout << "[PIEthernet] Not implemented, use \"PIEthernet::allAddresses\" or \"PIEthernet::interfaces\" instead"; return Address(); #else struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, interface_.dataAscii()); int s = ::socket(AF_INET, SOCK_DGRAM, 0); ioctl(s, SIOCGIFADDR, &ifr); ::close(s); struct sockaddr_in * sa = (struct sockaddr_in * )&ifr.ifr_addr; return Address(uint(sa->sin_addr.s_addr)); #endif } PIVector PIEthernet::allAddresses() { PIEthernet::InterfaceList il = interfaces(); PIVector
ret; bool has_127 = false; piForeachC (PIEthernet::Interface & i, il) { if (i.address == "127.0.0.1") has_127 = true; Address a(i.address); if (a.ip() == 0) continue; ret << a; } // piCout << "[PIEthernet::allAddresses]" << al; if (!has_127) ret << Address("127.0.0.1"); return ret; } // System wrap int PIEthernet::ethErrorCore() { #ifdef WINDOWS return WSAGetLastError(); #else return errno; #endif } PIString PIEthernet::ethErrorString() { #ifdef WINDOWS char * msg; int err = WSAGetLastError(); FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL); return "code " + PIString::fromNumber(err) + " - " + PIString(msg); #else return errorString(); #endif } int PIEthernet::ethRecv(int sock, void * buf, int size, int flags) { if (sock < 0) return -1; return recv(sock, #ifdef WINDOWS (char*) #endif buf, size, flags); } int PIEthernet::ethRecvfrom(int sock, void * buf, int size, int flags, sockaddr * addr) { if (sock < 0) return -1; #ifdef QNX return recv(sock, buf, size, flags); #else socklen_t len = sizeof(sockaddr); return recvfrom(sock, # ifdef WINDOWS (char*) # endif buf, size, flags, addr, &len); #endif } int PIEthernet::ethSendto(int sock, const void * buf, int size, int flags, sockaddr * addr, int addr_len) { if (sock < 0) return -1; return sendto(sock, #ifdef WINDOWS (const char*) #endif buf, size, flags, addr, addr_len); } void PIEthernet::ethClosesocket(int sock, bool shutdown) { //piCout << "close socket" << sock << shutdown; if (sock < 0) return; if (shutdown) ::shutdown(sock, #ifdef WINDOWS SD_BOTH); closesocket(sock); #else SHUT_RDWR); ::close(sock); #endif } int PIEthernet::ethSetsockopt(int sock, int level, int optname, const void * optval, int optlen) { if (sock < 0) return -1; return setsockopt(sock, level, optname, #ifdef WINDOWS (char*) #endif optval, optlen); } int PIEthernet::ethSetsockoptInt(int sock, int level, int optname, int value) { if (sock < 0) return -1; #ifdef WINDOWS DWORD #else int #endif so = value; return ethSetsockopt(sock, level, optname, &so, sizeof(so)); } int PIEthernet::ethSetsockoptBool(int sock, int level, int optname, bool value) { if (sock < 0) return -1; #ifdef WINDOWS BOOL #else int #endif so = (value ? 1 : 0); return ethSetsockopt(sock, level, optname, &so, sizeof(so)); }