1277 lines
37 KiB
C++
Executable File
1277 lines
37 KiB
C++
Executable File
/*
|
|
PIP - Platform Independent Primitives
|
|
Ethernet, UDP/TCP Broadcast/Multicast
|
|
Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "piincludes_p.h"
|
|
#include "piethernet.h"
|
|
#include "piconfig.h"
|
|
#include "pisysteminfo.h"
|
|
#ifdef QNX
|
|
# include <net/if.h>
|
|
# include <net/if_dl.h>
|
|
# include <hw/nicinfo.h>
|
|
# include <netdb.h>
|
|
# include <sys/socket.h>
|
|
# include <sys/time.h>
|
|
# include <sys/types.h>
|
|
# include <sys/ioctl.h>
|
|
# include <netinet/in.h>
|
|
# include <arpa/inet.h>
|
|
# include <ifaddrs.h>
|
|
# include <fcntl.h>
|
|
# ifdef BLACKBERRY
|
|
# include <netinet/in.h>
|
|
# else
|
|
# include <sys/dcmd_io-net.h>
|
|
# endif
|
|
# define ip_mreqn ip_mreq
|
|
# define imr_address imr_interface
|
|
#else
|
|
# ifdef WINDOWS
|
|
# include <io.h>
|
|
# include <winsock2.h>
|
|
# include <iphlpapi.h>
|
|
# include <psapi.h>
|
|
# include <ws2tcpip.h>
|
|
# define ip_mreqn ip_mreq
|
|
# define imr_address imr_interface
|
|
# else
|
|
# include <fcntl.h>
|
|
# include <sys/ioctl.h>
|
|
# include <netinet/in.h>
|
|
# include <arpa/inet.h>
|
|
# include <sys/socket.h>
|
|
# include <net/if.h>
|
|
# include <netdb.h>
|
|
# ifndef ANDROID
|
|
# include <ifaddrs.h>
|
|
# endif
|
|
# endif
|
|
#endif
|
|
#include <errno.h>
|
|
|
|
|
|
/** \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);
|
|
//if (type_ != UDP) init();
|
|
}
|
|
|
|
|
|
PIEthernet::PIEthernet(PIEthernet::Type type_, const PIString & ip_port, const PIFlags<PIEthernet::Parameters> 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();
|
|
closeDevice();
|
|
piMonitor.ethernets--;
|
|
//piCoutObj << "~PIEthernet done";
|
|
}
|
|
|
|
|
|
void PIEthernet::construct() {
|
|
//piCout << " PIEthernet" << uint(this);
|
|
setOption(BlockingWrite);
|
|
piMonitor.ethernets++;
|
|
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");
|
|
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<Parameters> 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;
|
|
}
|
|
|
|
|
|
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());
|
|
//Address::splitIPPort(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(addr_r.port());
|
|
PIFlags<Parameters> 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());
|
|
//cout << "!" << endl;
|
|
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();
|
|
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<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 (isClosed()) {
|
|
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();
|
|
//#ifndef QNX
|
|
//if (!params[Broadcast])
|
|
//;piCoutObj << "Warning: \"Broadcast\" parameter not set, \"joinMulticastGroup(\"" << group << "\")\" may be useless!";
|
|
addr_r.set(path());
|
|
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(addr_r.ipString());
|
|
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 = addr_r.ip();
|
|
/*#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<Parameters> params = parameters();
|
|
addr_r.set(path());
|
|
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 = addr_r.ip();
|
|
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;
|
|
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;
|
|
}
|
|
|
|
//#include <QDebug>
|
|
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
|
|
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_ << "...";
|
|
//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" << addr_r << "," << 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) {
|
|
addr_lr.set(uint(PRIVATE->raddr_.sin_addr.s_addr), ntohs(PRIVATE->raddr_.sin_port));
|
|
//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(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(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(addr_s.port());
|
|
/*if (params[PIEthernet::Broadcast]) PRIVATE->saddr_.sin_addr.s_addr = INADDR_BROADCAST;
|
|
else*/ 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) {
|
|
connected_ = false; {
|
|
init();
|
|
disconnected(true);
|
|
}
|
|
}
|
|
return ret;
|
|
default: break;
|
|
//return ::read(sock, read_to, max_size);
|
|
}
|
|
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() {
|
|
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<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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
PIEthernet::Address PIEthernet::interfaceAddress(const PIString & interface_) {
|
|
#ifdef WINDOWS
|
|
piCout << "[PIEthernet] Not implemented on Windows, 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::Address> 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();
|
|
PIVector<Address> 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;
|
|
//#endif
|
|
}
|
|
|
|
|
|
// 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));
|
|
}
|