/* PIP - Platform Independent Primitives Ethernet, UDP/TCP Broadcast/Multicast Copyright (C) 2017 Ivan Pelipenko peri4ko@yandex.ru 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 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "piincludes_p.h" #include "piethernet.h" #include "piconfig.h" #include "pisysteminfo.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 # ifndef ANDROID # 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 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); //if (type_ != UDP) init(); } PIEthernet::PIEthernet(PIEthernet::Type type_, const PIString & ip_port, const PIFlags params_): PIIODevice(ip_port, ReadWrite) { construct(); parseAddress(ip_port, &ip_, &port_); setType(type_); setParameters(params_); if (type_ != UDP) init(); } PIEthernet::PIEthernet(int sock_, PIString ip_port): PIIODevice("", ReadWrite) { construct(); parseAddress(ip_port, &ip_s, &port_s); 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(); closeDevice(); piMonitor.ethernets--; //piCoutObj << "~PIEthernet done"; } void PIEthernet::construct() { //piCout << " PIEthernet" << uint(this); piMonitor.ethernets++; connected_ = connecting_ = listen_threaded = server_bounded = false; port_ = port_s = port_r = 0; sock = sock_s = -1; setReadTimeout(10000.); setWriteTimeout(10000.); setTTL(64); setMulticastTTL(1); server_thread_.setData(this); server_thread_.setName("__S__server_thread"); setThreadedReadBufferSize(65536); 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); //if (type() == PIEthernet::TCP_Client) ethSetsockoptBool(sock, SOL_SOCKET, SO_KEEPALIVE); applyTimeouts(); applyOptInt(IPPROTO_IP, IP_TTL, TTL()); // piCoutObj << "inited" << path(); //cout << "inited " << sock << ": bc = " << params << endl; //fcntl(sock, F_SETFL, 0/*O_NONBLOCK*/); //piCoutObj << "init" << sock; return true; } void PIEthernet::parseAddress(const PIString & ipp, PIString * ip, int * port) { //piCout << "parse" << ipp; if (ip != 0) *ip = ipp.left(ipp.find(":")); if (port != 0) *port = ipp.right(ipp.length() - ipp.find(":") - 1).toInt(); } 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)); } 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)); } bool PIEthernet::openDevice() { if (connected_) return true; init(); if (sock == -1 || path().isEmpty()) return false; parseAddress(path(), &ip_, &port_); 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(port_); PIFlags params = parameters(); if (params[PIEthernet::Broadcast]) PRIVATE->addr_.sin_addr.s_addr = INADDR_ANY; else PRIVATE->addr_.sin_addr.s_addr = inet_addr(ip_.dataAscii()); #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 " << ip_ << ":" << port_ << ", " << ethErrorString(); return false; } opened_ = true; while (!mcast_queue.isEmpty()) joinMulticastGroup(mcast_queue.dequeue()); //cout << "!" << endl; applyTimeouts(); applyOptInt(IPPROTO_IP, IP_TTL, TTL()); port_r = 0; ip_r.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(); if (connected_) disconnected(false); connected_ = connecting_ = 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(); //#ifndef QNX //if (!params[Broadcast]) //;piCoutObj << "Warning: \"Broadcast\" parameter not set, \"joinMulticastGroup(\"" << group << "\")\" may be useless!"; parseAddress(path(), &ip_, &port_); struct ip_mreqn mreq; memset(&mreq, 0, sizeof(mreq)); #ifdef LINUX //mreq.imr_address.s_addr = INADDR_ANY; /*PIEthernet::InterfaceList il = interfaces(); const PIEthernet::Interface * ci = il.getByAddress(ip_); if (ci != 0) mreq.imr_ifindex = ci->index;*/ #endif if (params[PIEthernet::Broadcast]) mreq.imr_address.s_addr = INADDR_ANY; else mreq.imr_address.s_addr = inet_addr(ip_.dataAscii()); /*#ifndef WINDOWS PIEthernet::InterfaceList il = interfaces(); const PIEthernet::Interface * ci = il.getByAddress(ip_); if (ci != 0) mreq.imr_ifindex = ci->index; #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; //#else // parseAddress(path(), &ip_, &port_); // struct ip_mreq mreq; // memset(&mreq, 0, sizeof(mreq)); // mreq.imr_interface.s_addr = inet_addr(ip_.dataAscii()); // 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; //#endif 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(); /// TODO windows parseAddress(path(), &ip_, &port_); struct ip_mreqn mreq; memset(&mreq, 0, sizeof(mreq)); if (params[PIEthernet::Broadcast]) mreq.imr_address.s_addr = INADDR_ANY; else mreq.imr_address.s_addr = inet_addr(ip_.dataAscii()); 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() { connecting_ = true; return true; /*if (sock == -1) return false; memset(addr_, 0, sizeof(*addr_)); parseAddress(path_, &ip_, &port_); PRIVATE->addr_.sin_port = htons(port_); PRIVATE->addr_.sin_addr.s_addr = inet_addr(ip_.data()); PRIVATE->addr_.sin_family = AF_INET; #ifdef QNX PRIVATE->addr_.sin_len = sizeof(*addr_); #endif //piCoutObj << "connect to " << ip << ":" << port_; connected_ = (::connect(sock, (sockaddr * )addr_, sizeof(*addr_)) == 0); if (!connected_) piCoutObj << "Can`t connect to " << ip_ << ":" << port_ << ", " << ethErrorString(); opened_ = connected_; if (connected_) 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; parseAddress(path(), &ip_, &port_); memset(&PRIVATE->addr_, 0, sizeof(PRIVATE->addr_)); PRIVATE->addr_.sin_port = htons(port_); PRIVATE->addr_.sin_addr.s_addr = inet_addr(ip_.dataAscii()); 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 " << ip_ << ":" << port_ << ", " << ethErrorString(); return false; } if (::listen(sock, 64) == -1) { piCoutObj << "Can`t listen on "<< ip_ << ":" << port_ << ", " << ethErrorString(); return false; } opened_ = server_bounded = true; //piCoutObj << "listen on " << ip_ << ":" << port_; server_thread_.start(server_func); return true; } //#include 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(1); 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 memset(&PRIVATE->addr_, 0, sizeof(PRIVATE->addr_)); parseAddress(path(), &ip_, &port_); PRIVATE->addr_.sin_port = htons(port_); PRIVATE->addr_.sin_addr.s_addr = inet_addr(ip_.dataAscii()); PRIVATE->addr_.sin_family = AF_INET; #ifdef QNX PRIVATE->addr_.sin_len = sizeof(PRIVATE->addr_); #endif //piCoutObj << "connect to " << ip_ << ":" << port_ << "..."; //qDebug() << "connect to " << ip_.data() << ":" << port_ << sock << PRIVATE->addr_.sin_addr.s_addr << "..."; connected_ = (::connect(sock, (sockaddr * )&(PRIVATE->addr_), sizeof(PRIVATE->addr_)) == 0); //piCoutObj << "connect to " << ip_ << ":" << port_ << connected_; //qDebug() << "connect to " << ip_.data() << ":" << port_ << connected_; if (!connected_) piCoutObj << "Can`t connect to " << ip_ << ":" << port_ << ", " << ethErrorString(); opened_ = connected_; if (connected_) { connecting_ = false; connected(); } else piMSleep(10); //piCout << "connected to" << path(); //qDebug() << "connected to" << path().data(); } if (!connected_) return -1; errorClear(); rs = ethRecv(sock, read_to, max_size); //piCoutObj << "readed" << rs; //qDebug() << "readed" << rs; if (rs <= 0) { lerr = ethErrorCore(); //piCoutObj << "readed error" << lerr << errorString().data() << parameters()[DisonnectOnTimeout]; #ifdef WINDOWS if ((lerr == WSAEWOULDBLOCK || lerr == WSAETIMEDOUT) && !parameters()[DisonnectOnTimeout]) { #else if ((lerr == EWOULDBLOCK || lerr == EAGAIN) && !parameters()[DisonnectOnTimeout]) { #endif //piCoutObj << errorString(); piMSleep(10); return -1; } if (connected_) { init(); connected_ = false; disconnected(rs < 0); } if (parameters()[KeepConnection]) connect(); //piCoutObj << "eth" << ip_ << "disconnected"; } if (rs > 0) received(read_to, rs); //qDebug() << "return from read" << 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) { port_r = ntohs(PRIVATE->raddr_.sin_port); ip_r = PIStringAscii(inet_ntoa(PRIVATE->raddr_.sin_addr)); //piCoutObj << "read from" << ip_r << ":" << port_r << rs << "bytes"; //piCout << "received from" << lastReadAddress(); received(read_to, rs); } //else piCoutObj << "read returt" << rs << ", error" << ethErrorString(); return rs; //return ::read(sock, read_to, max_size); default: break; //return ::read(sock, (char * )read_to, max_size); } 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(port_s); PRIVATE->addr_.sin_addr.s_addr = inet_addr(ip_s.dataAscii()); 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(1); 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(port_s); /*if (params[PIEthernet::Broadcast]) PRIVATE->saddr_.sin_addr.s_addr = INADDR_BROADCAST; else*/ PRIVATE->saddr_.sin_addr.s_addr = inet_addr(ip_s.dataAscii()); 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, 0, (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_)); parseAddress(path(), &ip_, &port_); PRIVATE->addr_.sin_port = htons(port_); PRIVATE->addr_.sin_addr.s_addr = inet_addr(ip_.dataAscii()); 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 " << ip_ << ":" << port_ << ", " << ethErrorString(); opened_ = connected_; if (connected_) { connecting_ = false; connected(); } } if (!connected_) return -1; ret = ::send(sock, (const char *)data, max_size, 0); if (ret < 0) { connected_ = false; { init(); disconnected(true); } } return ret; default: break; //return ::read(sock, read_to, max_size); } return -1; } void PIEthernet::clientDeleted() { clients_mutex.lock(); clients_.removeOne((PIEthernet*)emitter()); 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) { #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; //char d[256]; //cout << " recv " << recv(s, d, 256, 0) << endl; //cout << recv(ce->clients_.back()->sock, d, 256, 0) << 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;}} } } 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 (ret->ifa_flags & IFF_BROADCAST) ci.flags |= PIEthernet::ifBroadcast; //if (ret->ifa_flags & IFF_MULTICAST) ci.flags |= PIEthernet::ifMulticast; 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; } /*if (ci.flags[PIEthernet::ifBroadcast]) ci.broadcast = getSockAddr(ret->ifa_broadaddr); if (ci.flags[PIEthernet::ifPTP]) ci.ptp = getSockAddr(ret->ifa_dstaddr);*/ pAdapter = pAdapter->Next; } } else piCout << "[PIEthernet] GetAdaptersInfo failed with error: " << ret; if (pAdapterInfo) HeapFree(GetProcessHeap(), 0, (pAdapterInfo)); #else /*# ifdef QNX PIStringList il, sl; PIProcess proc; proc.setGrabOutput(true); proc.exec(ifconfigPath.c_str(), "-l"); if (!proc.waitForFinish(1000)) return sl; PIString out(proc.readOutput()); il = out.split(" "); il.removeAll(""); piForeachC (PIString & i, il) { proc.exec(ifconfigPath.c_str(), i); if (!proc.waitForFinish(1000)) return il; sl << i.trimmed(); out = proc.readOutput(); int al = out.length(); al = (al - out.replaceAll("alias", "").length()) / 5; for (int j = 0; j < al; ++j) sl << i.trimmed() + ":" + PIString::fromNumber(j); } return sl; # else PIStringList sl; PIProcess proc; proc.setGrabOutput(true); proc.exec(ifconfigPath.c_str(), "-s"); if (!proc.waitForFinish(1000)) return sl; PIString out(proc.readOutput()); out.cutLeft(out.find('\n') + 1); while (!out.isEmpty()) { sl << out.left(out.find(' ')); out.cutLeft(out.find('\n') + 1); } return sl; # endif*/ # 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) { 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); il << ci; } delete ifc.ifc_buf; # else struct ifaddrs * ret; int s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (getifaddrs(&ret) == 0) { while (ret != 0) { if (ret->ifa_addr == 0) { ret = ret->ifa_next; continue; } if (ret->ifa_addr->sa_family != AF_INET) { ret = ret->ifa_next; continue; } ci.name = PIString(ret->ifa_name); ci.address = getSockAddr(ret->ifa_addr); ci.netmask = getSockAddr(ret->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, ret->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 (ret->ifa_flags & IFF_UP) ci.flags |= PIEthernet::ifActive; if (ret->ifa_flags & IFF_RUNNING) ci.flags |= PIEthernet::ifRunning; if (ret->ifa_flags & IFF_BROADCAST) ci.flags |= PIEthernet::ifBroadcast; if (ret->ifa_flags & IFF_MULTICAST) ci.flags |= PIEthernet::ifMulticast; if (ret->ifa_flags & IFF_LOOPBACK) ci.flags |= PIEthernet::ifLoopback; if (ret->ifa_flags & IFF_POINTOPOINT) ci.flags |= PIEthernet::ifPTP; ci.broadcast.clear(); ci.ptp.clear(); if (ci.flags[PIEthernet::ifBroadcast]) ci.broadcast = getSockAddr(ret->ifa_broadaddr); if (ci.flags[PIEthernet::ifPTP]) ci.ptp = getSockAddr(ret->ifa_dstaddr); ci.index = if_nametoindex(ret->ifa_name); il << ci; ret = ret->ifa_next; } freeifaddrs(ret); } else piCout << "[PIEthernet] Can`t get interfaces:" << errorString(); if (s != -1) ::close(s); # endif #endif return il; } PIString PIEthernet::interfaceAddress(const PIString & interface_) { #ifdef WINDOWS piCout << "[PIEthernet] Not implemented on Windows, use \"PIEthernet::allAddresses\" or \"PIEthernet::interfaces\" instead"; return PIString(); #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 PIStringAscii(inet_ntoa(sa->sin_addr)); #endif } PIStringList PIEthernet::allAddresses() { /*#ifdef WINDOWS PIStringList al; PIString ca; 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 PIStringList(); } 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 PIStringList(); } } if ((ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) { pAdapter = pAdapterInfo; while (pAdapter) { ca = PIString(pAdapter->IpAddressList.IpAddress.String); if (ca != "0.0.0.0") al << ca; pAdapter = pAdapter->Next; } } else piCout << "[PIEthernet] GetAdaptersInfo failed with error: " << ret; if (pAdapterInfo) HeapFree(GetProcessHeap(), 0, (pAdapterInfo)); return al; #else*/ PIEthernet::InterfaceList il = interfaces(); PIStringList al; piForeachC (PIEthernet::Interface & i, il) al << i.address; // piCout << "[PIEthernet::allAddresses]" << al; if (!al.contains("127.0.0.1")) al << "127.0.0.1"; return al.removeStrings("0.0.0.0"); //#endif } PIString PIEthernet::resolve(const PIString & host) { PIString ip(host), port; int i = host.find(':'); if (i >= 0) { ip = host.left(i); port = host.right(host.length() - i - 1); } PIString ret = PIStringAscii("0.0.0.0"); if (!port.isEmpty()) ret += PIStringAscii(":") + port; //#ifdef WINDOWS hostent * he = gethostbyname(ip.dataAscii()); if (!he) return ret; struct in_addr addr; if (he->h_addr_list[0]) { addr.s_addr = *((uint*)(he->h_addr_list[0])); ret = inet_ntoa(addr); if (!port.isEmpty()) ret += PIStringAscii(":") + port; } //#else //#endif 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)); }