/* PIP - Platform Independent Primitives Ethernet, UDP Copyright (C) 2013 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 . */ #include "piethernet.h" PIEthernet::PIEthernet(void * data, ReadRetFunc slot): PIIODevice("", ReadWrite) { piMonitor.ethernets++; setPriority(piHigh); type_ = UDP; ret_data_ = data; ip_ = ip_s = ""; port_ = port_s = 0; sock = sock_s = -1; ret_func_ = slot; connected_ = false; params = PIEthernet::ReuseAddress; server_thread_.setData(this); setThreadedReadBufferSize(65536); if (type_ != UDP) init(); } PIEthernet::PIEthernet(PIEthernet::Type type, void * data, ReadRetFunc slot): PIIODevice("", ReadWrite) { piMonitor.ethernets++; setPriority(piHigh); type_ = type; ret_data_ = data; ip_ = ip_s = ""; port_ = port_s = 0; sock = sock_s = -1; ret_func_ = slot; connected_ = false; params = PIEthernet::ReuseAddress; server_thread_.setData(this); setThreadedReadBufferSize(65536); if (type_ != UDP) init(); } PIEthernet::PIEthernet(int sock_, PIString ip_port): PIIODevice("", ReadWrite) { piMonitor.ethernets++; setPriority(piHigh); type_ = TCP_Client; parseAddress(ip_port, &ip_s, &port_s); sock = sock_; sock_s = -1; server_thread_.setData(this); params = PIEthernet::ReuseAddress; init_ = opened_ = connected_ = true; setThreadedReadBufferSize(65536); } PIEthernet::~PIEthernet() { piMonitor.ethernets--; if (server_thread_.isRunning()) server_thread_.terminate(); closeSocket(sock); //if (buffer_ != 0) delete buffer_; //buffer_ = 0; } bool PIEthernet::init() { //cout << "init " << type_ << endl; closeSocket(sock); int st = 0; #ifdef WINDOWS int flags = WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF; #else int so = 1; #endif if (type_ == UDP) st = SOCK_DGRAM; else st = SOCK_STREAM; #ifdef WINDOWS if (params[ReuseAddress]) flags |= WSA_FLAG_OVERLAPPED; sock = WSASocket(AF_INET, st, type_ == UDP ? IPPROTO_UDP : IPPROTO_TCP, NULL, 0, flags); #else sock = socket(AF_INET, st, type_ == UDP ? IPPROTO_UDP : IPPROTO_TCP); #endif if (sock == -1) { piCout << "[PIEthernet] Cant`t create socket, " << EthErrorString() << endl; return false; } #ifndef WINDOWS 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)); #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; if (sock == -1) init(); if (sock == -1 || path_.isEmpty()) return false; parseAddress(path_, &ip_, &port_); //cout << " bind to " << sock << ": " << (params[PIEthernet::Broadcast] ? "0.0.0.0" : path_) << ": " << port_ << " ..." <= 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()) { //piCout << "[PIEthernet] Can`t send to uninitialized socket" << endl; return -1; } //piCout << "[PIEthernet] sending to " << ip_s << ":" << port_s << " " << max_size << " bytes" << endl; 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 if (::connect(sock, (sockaddr * )&addr_, sizeof(addr_)) != 0) { piCout << "[PIEthernet] Cant`t connect to " << ip_s << ":" << port_s << ", " << EthErrorString() << endl; return -1; } ret = ::write(sock, data, max_size); 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; #ifdef WINDOWS return sendto(sock, (const char * )data, max_size, 0, (sockaddr * )&saddr_, sizeof(saddr_)); #else return sendto(sock, data, max_size, 0, (sockaddr * )&saddr_, sizeof(saddr_)); #endif case TCP_Client: return ::write(sock, data, max_size); 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) { piCout << "[PIEthernet] Cant`t accept new connection, " << EthErrorString() << endl; return; } PIString ip(inet_ntoa(client_addr.sin_addr)); ip += ":" + PIString::fromNumber(htons(client_addr.sin_port)); ce->clients_ << new PIEthernet(s, ip); //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; } PIStringList PIEthernet::interfaces() { #ifdef WINDOWS piCout << "[PIEthernet] Not implemented on Windows, use \"PIEthernet::allAddresses\" instead" << endl; return PIStringList(); #else # ifdef QNX PIStringList il, sl; /*struct if_nameindex * ni = if_nameindex(); for (int i = 0; ; ++i) { if (ni[i].if_name == 0 || ni[i].if_index == 0) break; sl << PIString(ni[i].if_name); } if_freenameindex(ni);*/ 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); } //cout << out; //cout << sl << endl; return sl; # else PIStringList sl; /*struct if_nameindex * ni = if_nameindex(); for (int i = 0; ; ++i) { if (ni[i].if_name == 0 || ni[i].if_index == 0) break; sl << PIString(ni[i].if_name); } if_freenameindex(ni);*/ PIProcess proc; proc.setGrabOutput(true); proc.exec(ifconfigPath.c_str(), "-s"); if (!proc.waitForFinish(1000)) return sl; PIString out(proc.readOutput()); //cout << out << endl; out.cutLeft(out.find('\n') + 1); while (!out.isEmpty()) { sl << out.left(out.find(' ')); out.cutLeft(out.find('\n') + 1); } //cout << sl << endl; return sl; # endif #endif } PIString PIEthernet::interfaceAddress(const PIString & interface_) { #ifdef WINDOWS piCout << "[PIEthernet] Not implemented on Windows, use \"PIEthernet::allAddresses\" instead" << endl; 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" << endl; 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" << endl; return PIStringList(); } } if ((ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) { pAdapter = pAdapterInfo; while (pAdapter) { /*if (pAdapter->Type != MIB_IF_TYPE_ETHERNET && pAdapter->Type != MIB_IF_TYPE_LOOPBACK) { pAdapter = pAdapter->Next; continue; }*/ 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 << endl; if (pAdapterInfo) HeapFree(GetProcessHeap(), 0, (pAdapterInfo)); return al; #else PIStringList il = interfaces(), al; piForeachC (PIString & i, il) al << interfaceAddress(i); return al; #endif }