Files
pip/piethernet.cpp

837 lines
26 KiB
C++

/*
PIP - Platform Independent Primitives
Ethernet, UDP/TCP Broadcast/Multicast
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
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 <http://www.gnu.org/licenses/>.
*/
#include "piethernet.h"
#include "piconfig.h"
#ifdef QNX
# include <net/if_dl.h>
# include <hw/nicinfo.h>
# include <sys/dcmd_io-net.h>
#endif
/** \class PIEthernet
* \brief Ethernet device
* \details
* \section PIEthernet_sec0 Synopsis
* %PIEthernet designed to work with IPv4 network by two protocols:
* UDP and TCP. This class allow you send and receive packets to/from
* another computer through network.
*
* \section PIEthernet_sec1 IPv4
*
*
* \section PIEthernet_sec2 UDP
* User Datagram Protocol
*
* \section PIEthernet_sec3 TCP
* Transmission Control Protocol
*
* */
REGISTER_DEVICE(PIEthernet);
PIEthernet::PIEthernet(): PIIODevice("", ReadWrite) {
piMonitor.ethernets++;
ip_ = ip_s = "";
port_ = port_s = 0;
sock = sock_s = -1;
connected_ = connecting_ = false;
server_thread_.setData(this);
setThreadedReadBufferSize(65536);
setPriority(piHigh);
setType(UDP);
setParameters(PIEthernet::ReuseAddress);
//if (type_ != UDP) init();
}
PIEthernet::PIEthernet(PIEthernet::Type type_, const PIString & ip_port, const PIFlags<PIEthernet::Parameters> params_): PIIODevice(ip_port, ReadWrite) {
piMonitor.ethernets++;
parseAddress(ip_port, &ip_, &port_);
ip_s = "";
port_s = 0;
sock = sock_s = -1;
connected_ = connecting_ = false;
server_thread_.setData(this);
setThreadedReadBufferSize(65536);
setPriority(piHigh);
setType(type_);
setParameters(params_);
if (type_ != UDP) init();
}
PIEthernet::PIEthernet(int sock_, PIString ip_port): PIIODevice("", ReadWrite) {
piMonitor.ethernets++;
parseAddress(ip_port, &ip_s, &port_s);
sock = sock_;
sock_s = -1;
init_ = opened_ = connected_ = true;
connecting_ = false;
server_thread_.setData(this);
setParameters(PIEthernet::ReuseAddress);
setThreadedReadBufferSize(65536);
setPriority(piHigh);
setType(TCP_Client, false);
setPath(ip_port);
}
PIEthernet::~PIEthernet() {
piMonitor.ethernets--;
if (server_thread_.isRunning()) server_thread_.terminate();
stop();
closeSocket(sock);
closeSocket(sock_s);
//if (buffer_ != 0) delete buffer_;
//buffer_ = 0;
}
bool PIEthernet::init() {
//cout << "init " << type_ << endl;
closeSocket(sock);
closeSocket(sock_s);
int st = 0, pr = 0;
#ifdef WINDOWS
int flags = WSA_FLAG_OVERLAPPED;
#else
int so = 1;
#endif
if (type() == UDP) {
st = SOCK_DGRAM;
pr = IPPROTO_UDP;
} else {
st = SOCK_STREAM;
pr = IPPROTO_TCP;
}
#ifdef WINDOWS
if (type() == UDP) flags = WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF;
sock = WSASocket(AF_INET, st, pr, NULL, 0, flags);
sock_s = WSASocket(AF_INET, st, pr, NULL, 0, WSA_FLAG_OVERLAPPED);
#else
sock = socket(AF_INET, st, pr);
sock_s = socket(AF_INET, st, pr);
#endif
if (sock == -1 || sock_s == -1) {
piCoutObj << "Can`t create socket, " << ethErrorString();
return false;
}
PIFlags<Parameters> params = parameters();
#ifndef WINDOWS
timeval to;
to.tv_sec = 10;
to.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof(to));
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &to, sizeof(to));
if (params[PIEthernet::ReuseAddress]) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &so, sizeof(so));
if (params[PIEthernet::Broadcast]) setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &so, sizeof(so));
#else
BOOL bv = TRUE;
if (params[PIEthernet::ReuseAddress]) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char * )&bv, sizeof(bv));
#endif
//cout << "inited " << sock << ": bc = " << params << endl;
//fcntl(sock, F_SETFL, 0/*O_NONBLOCK*/);
return true;
}
void PIEthernet::parseAddress(const PIString & ipp, PIString * ip, int * port) {
if (ip != 0) *ip = ipp.left(ipp.find(":"));
if (port != 0) *port = ipp.right(ipp.length() - ipp.find(":") - 1).toInt();
}
bool PIEthernet::openDevice() {
if (connected_) return true;
init();
if (sock == -1 || path().isEmpty()) return false;
parseAddress(path(), &ip_, &port_);
if (type() != UDP && mode() == PIIODevice::WriteOnly)
return true;
//piCout << "bind to" << (params[PIEthernet::Broadcast] ? "bc" : ip_) << ":" << port_ << " ...";
memset(&addr_, 0, sizeof(addr_));
addr_.sin_family = AF_INET;
addr_.sin_port = htons(port_);
PIFlags<Parameters> params = parameters();
if (params[PIEthernet::Broadcast]) addr_.sin_addr.s_addr = INADDR_ANY;
else addr_.sin_addr.s_addr = inet_addr(ip_.data());
#ifdef QNX
addr_.sin_len = sizeof(addr_);
#endif
int tries = 0;
while ((bind(sock, (sockaddr * )&addr_, sizeof(addr_)) == -1) && (tries < 10)) {
init();
tries++;
}
if (tries == 10) {
piCoutObj << "Can`t bind to " << ip_ << ":" << port_ << ", " << ethErrorString();
return false;
}
opened_ = true;
while (!mcast_queue.isEmpty())
joinMulticastGroup(mcast_queue.dequeue());
//cout << "!" << endl;
return true;
}
bool PIEthernet::closeDevice() {
//cout << "close\n";
closeSocket(sock);
closeSocket(sock_s);
piForeach (PIEthernet * i, clients_)
delete i;
clients_.clear();
if (server_thread_.isRunning()) server_thread_.terminate();
if (connected_) disconnected(false);
connected_ = false;
return true;
}
void PIEthernet::closeSocket(int & sd) {
if (sd != -1) {
#ifdef WINDOWS
shutdown(sd, SD_BOTH);
closesocket(sd);
#else
shutdown(sd, SHUT_RDWR);
::close(sd);
#endif
}
sd = -1;
}
void PIEthernet::setParameter(PIEthernet::Parameters parameter, bool on) {
PIFlags<Parameters> cp = (PIFlags<Parameters>)(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 (!opened_) {
if (mcast_queue.contains(group))
return false;
mcast_queue.enqueue(group);
if (!mcast_groups.contains(group)) mcast_groups << group;
return true;
}
PIFlags<Parameters> params = parameters();
#ifdef WINDOWS
parseAddress(path(), &ip_, &port_);
memset(&addr_, 0, sizeof(addr_));
addr_.sin_family = AF_INET;
addr_.sin_addr.s_addr = inet_addr(group.data());
//int so = 1;
//setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char * )&so, sizeof(so));
SOCKET ret = WSAJoinLeaf(sock, (sockaddr *)&addr_, sizeof(addr_), NULL, NULL, NULL, NULL, JL_BOTH);
if (ret == INVALID_SOCKET) {
piCoutObj << "Can`t join multicast group " << group << ", " << ethErrorString();
return false;
}
if (ret != sock) {
leafs.insert(group, ret);
if (!mcast_groups.contains(group)) mcast_groups << group;
}
#else
# 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));
mreq.imr_ifindex = 0;
if (params[PIEthernet::Broadcast]) mreq.imr_address.s_addr = INADDR_ANY;
else mreq.imr_address.s_addr = inet_addr(ip_.data());
PIEthernet::InterfaceList il = interfaces();
const PIEthernet::Interface * ci = il.getByAddress(ip_);
if (ci != 0) mreq.imr_ifindex = ci->index;
//piCout << "join group" << group << "ip" << ip_ << "with index" << mreq.imr_ifindex;
mreq.imr_multiaddr.s_addr = inet_addr(group.data());
int so = 1;
//setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &so, sizeof(so));
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &so, sizeof(so));
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq));
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0) {
piCoutObj << "Can`t join multicast group " << group << ", " << ethErrorString();
return false;
}
if (!mcast_groups.contains(group)) mcast_groups << group;
# endif
#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<Parameters> params = parameters();
#ifdef WINDOWS
SOCKET s = leafs[group];
if (s != SOCKET()) {
leafs.erase(group);
closesocket(s);
mcast_groups.removeAll(group);
}
#else
# ifndef QNX
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_.data());
mreq.imr_multiaddr.s_addr = inet_addr(group.data());
mreq.imr_ifindex = 0;
if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
piCoutObj << "Can`t leave multicast group " << group << ", " << ethErrorString();
return false;
}
mcast_groups.removeAll(group);
#endif
#endif
return true;
}
bool PIEthernet::connect() {
connecting_ = true;
return true;
/*if (sock == -1) return false;
memset(&addr_, 0, sizeof(addr_));
parseAddress(path_, &ip_, &port_);
addr_.sin_port = htons(port_);
addr_.sin_addr.s_addr = inet_addr(ip_.data());
addr_.sin_family = AF_INET;
#ifdef QNX
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() {
if (sock == -1) init();
if (sock == -1) return false;
parseAddress(path(), &ip_, &port_);
memset(&addr_, 0, sizeof(addr_));
addr_.sin_port = htons(port_);
addr_.sin_addr.s_addr = inet_addr(ip_.data());
addr_.sin_family = AF_INET;
#ifdef QNX
addr_.sin_len = sizeof(addr_);
#endif
opened_ = false;
int tries = 0;
while ((bind(sock, (sockaddr * )&addr_, sizeof(addr_)) == -1) && (tries < 10)) {
init();
tries++;
}
if (tries == 10) {
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_ = true;
//piCoutObj << "listen on " << ip_ << ":" << port_;
server_thread_.start(server_func);
return true;
}
int PIEthernet::read(void * read_to, int max_size) {
//cout << "read " << sock << endl;
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_ << endl;
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 = recv(s, (char * )read_to, max_size, 0);
closeSocket(s);
return rs;
case TCP_Client:
if (connecting_) {
memset(&addr_, 0, sizeof(addr_));
parseAddress(path(), &ip_, &port_);
addr_.sin_port = htons(port_);
addr_.sin_addr.s_addr = inet_addr(ip_.data());
addr_.sin_family = AF_INET;
#ifdef QNX
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_) {
connecting_ = false;
connected();
}
}
if (!connected_) return -1;
#ifdef WINDOWS
rs = recv(sock, (char * )read_to, max_size, 0);
#else
rs = recv(sock, read_to, max_size, MSG_DONTWAIT);
#endif
if (rs <= 0) {
#ifdef WINDOWS
lerr = WSAGetLastError();
if (lerr == WSAEWOULDBLOCK) {
piMSleep(1);
return -1;
}
#else
lerr = errno;
if (lerr == EAGAIN || lerr == EWOULDBLOCK) {
piMSleep(1);
return -1;
}
#endif
if (connected_) disconnected(rs < 0);
connected_ = false;
//piCoutObj << "eth" << path_ << "disconnected";
}
if (rs > 0) received(read_to, rs);
return rs;
case UDP:
#ifdef WINDOWS
rs = recv(sock, (char * )read_to, max_size, 0);
#else
rs = recv(sock, read_to, max_size, 0);
#endif
//piCout << "eth" << path_ << "read return" << rs << errno;
if (rs > 0) received(read_to, rs);
return rs;
//return ::read(sock, read_to, max_size);
default: break;
//return ::read(sock, (char * )read_to, max_size);
}
return -1;
}
int PIEthernet::write(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(&addr_, 0, sizeof(addr_));
addr_.sin_port = htons(port_s);
addr_.sin_addr.s_addr = inet_addr(ip_s.data());
addr_.sin_family = AF_INET;
#ifdef QNX
addr_.sin_len = sizeof(addr_);
#endif
//piCoutObj << "connect SingleTCP" << ip_s << ":" << port_s << "...";
if (::connect(sock, (sockaddr * )&addr_, sizeof(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:
saddr_.sin_port = htons(port_s);
/*if (params[PIEthernet::Broadcast]) saddr_.sin_addr.s_addr = INADDR_BROADCAST;
else*/ saddr_.sin_addr.s_addr = inet_addr(ip_s.data());
saddr_.sin_family = AF_INET;
//piCout << "[PIEth] write to" << ip_s << ":" << port_s << max_size << "bytes ...";
#ifdef WINDOWS
return sendto(sock_s, (const char * )data, max_size, 0, (sockaddr * )&saddr_, sizeof(saddr_));
#else
return sendto(sock_s, data, max_size, 0, (sockaddr * )&saddr_, sizeof(saddr_));
#endif
//piCout << "[PIEth] write to" << ip_s << ":" << port_s << "ok";
case TCP_Client:
if (connecting_) {
memset(&addr_, 0, sizeof(addr_));
parseAddress(path(), &ip_, &port_);
addr_.sin_port = htons(port_);
addr_.sin_addr.s_addr = inet_addr(ip_.data());
addr_.sin_family = AF_INET;
#ifdef QNX
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_) {
connecting_ = false;
connected();
}
}
if (!connected_) return -1;
return ::send(sock, (const char *)data, max_size, 0);
default: break;
//return ::read(sock, read_to, max_size);
}
return -1;
}
void PIEthernet::server_func(void * eth) {
PIEthernet * ce = (PIEthernet * )eth;
sockaddr_in client_addr;
socklen_t slen = sizeof(client_addr);
int s = accept(ce->sock, (sockaddr * )&client_addr, &slen);
if (s == -1) {
if (ce->debug()) piCout << "[PIEthernet] Can`t accept new connection, " << ethErrorString();
return;
}
PIString ip(inet_ntoa(client_addr.sin_addr));
ip += ":" + PIString::fromNumber(htons(client_addr.sin_port));
ce->clients_ << new PIEthernet(s, ip);
ce->newConnection(ce->clients_.back());
//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<PIString>("ip", readIP(), em, ep));
setReadPort(readDeviceSetting<int>("port", readPort(), em, ep));
setParameter(PIEthernet::Broadcast, readDeviceSetting<bool>("broadcast", isParameterSet(PIEthernet::Broadcast), em, ep));
setParameter(PIEthernet::ReuseAddress, readDeviceSetting<bool>("reuseAddress", isParameterSet(PIEthernet::ReuseAddress), em, ep));
return true;
}
PIString PIEthernet::constructFullPath() const {
PIString ret(fullPathPrefix() + "://");
ret << (type() == PIEthernet::UDP ? "UDP" : "TCP") << ":" << readIP() << ":" << readPort();
if (type() == PIEthernet::UDP) {
piForeachC (PIString & m, multicastGroups())
ret << ":mcast:" << m;
}
return ret;
}
void PIEthernet::configureFromFullPath(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;
}
if (i <= 2) 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;
#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.address = PIString(pAdapter->IpAddressList.IpAddress.String);
if (ci.address == "0.0.0.0") {
pAdapter = pAdapter->Next;
continue;
}
ci.mac = macFromBytes(PIByteArray(pAdapter->Address, pAdapter->AddressLength));
ci.netmask = PIString(pAdapter->IpAddressList.IpMask.String);
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();
/*if (ci.flags[PIEthernet::ifBroadcast])
ci.broadcast = getSockAddr(ret->ifa_broadaddr);
if (ci.flags[PIEthernet::ifPTP])
ci.ptp = getSockAddr(ret->ifa_dstaddr);*/
il << ci;
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(ifc.ifc_req[i].ifr_name);
if (in.isEmpty()) continue;
ci.name = in;
strcpy(ir.ifr_name, in.data());
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);
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->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
int fd = ::open((PIString("/dev/io-net/") + ci.name).data(), 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));
}
# else
# ifdef MAC_OS
PIString req = PIString(ifconfigPath) + " " + ci.name + " | grep ether";
FILE * fp = popen(req.data(), "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));
}
# 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_.data());
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 PIString(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;
return al.removeStrings("0.0.0.0");
//#endif
}