/// \file socket.cpp /// A handy Socket wrapper library. /// Written by Jaron Vietor in 2010 for DDVTech #include "socket.h" #include "defines.h" #include "timing.h" #include #include #include #include #include #include #include #include #define BUFFER_BLOCKSIZE 4096 // set buffer blocksize to 4KiB #ifdef __CYGWIN__ #define SOCKETSIZE 8092ul #else #define SOCKETSIZE 51200ul #endif /// Local-scope only helper function that prints address families static const char* addrFam(int f){ switch(f){ case AF_UNSPEC: return "Unspecified"; case AF_INET: return "IPv4"; case AF_INET6: return "IPv6"; case PF_UNIX: return "Unix"; default: return "???"; } } static std::string getIPv6BinAddr(const struct sockaddr_in6 & remoteaddr){ char tmpBuffer[17] = "\000\000\000\000\000\000\000\000\000\000\377\377\000\000\000\000"; switch (remoteaddr.sin6_family){ case AF_INET: memcpy(tmpBuffer + 12, &(reinterpret_cast(&remoteaddr)->sin_addr.s_addr), 4); break; case AF_INET6: memcpy(tmpBuffer, &(remoteaddr.sin6_addr.s6_addr), 16); break; default: return ""; break; } return std::string(tmpBuffer, 16); } /// Helper function that matches two binary-format IPv6 addresses with prefix bits of prefix. bool Socket::matchIPv6Addr(const std::string &A, const std::string &B, uint8_t prefix){ if (!prefix){prefix = 128;} if (Util::Config::printDebugLevel >= DLVL_MEDIUM){ std::string Astr, Bstr; Socket::hostBytesToStr(A.data(), 16, Astr); Socket::hostBytesToStr(B.data(), 16, Bstr); MEDIUM_MSG("Matching: %s to %s with %u prefix", Astr.c_str(), Bstr.c_str(), prefix); } if (memcmp(A.data(), B.data(), prefix / 8)){return false;} if ((prefix % 8) && ((A.data()[prefix / 8] & (0xFF << (8 - (prefix % 8)))) != (B.data()[prefix / 8] & (0xFF << (8 - (prefix % 8)))))){ return false; } return true; } /// Attempts to match the given address with optional subnet to the given binary-form IPv6 address. /// Returns true if match could be made, false otherwise. bool Socket::isBinAddress(const std::string &binAddr, std::string addr){ //Check if we need to do prefix matching uint8_t prefixLen = 0; if (addr.find('/') != std::string::npos){ prefixLen = atoi(addr.c_str() + addr.find('/') + 1); addr.erase(addr.find('/'), std::string::npos); } //Loops over all IPs for the given address and matches them in IPv6 form. struct addrinfo *result, *rp, hints; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED; hints.ai_protocol = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; int s = getaddrinfo(addr.c_str(), 0, &hints, &result); if (s != 0){return false;} for (rp = result; rp != NULL; rp = rp->ai_next){ std::string tBinAddr = getIPv6BinAddr(*((sockaddr_in6 *)rp->ai_addr)); if (rp->ai_family == AF_INET){ if (matchIPv6Addr(tBinAddr, binAddr, prefixLen ? prefixLen + 96 : 0)){return true;} }else{ if (matchIPv6Addr(tBinAddr, binAddr, prefixLen)){return true;} } } freeaddrinfo(result); return false; } /// Converts the given address with optional subnet to binary IPv6 form. /// Returns 16 bytes of address, followed by 1 byte of subnet bits, zero or more times. std::string Socket::getBinForms(std::string addr){ //Check if we need to do prefix matching uint8_t prefixLen = 128; if (addr.find('/') != std::string::npos){ prefixLen = atoi(addr.c_str() + addr.find('/') + 1); addr.erase(addr.find('/'), std::string::npos); } //Loops over all IPs for the given address and converts to IPv6 binary form. struct addrinfo *result, *rp, hints; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED; hints.ai_protocol = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; int s = getaddrinfo(addr.c_str(), 0, &hints, &result); if (s != 0){return "";} std::string ret; for (rp = result; rp != NULL; rp = rp->ai_next){ ret += getIPv6BinAddr(*((sockaddr_in6 *)rp->ai_addr)); if (rp->ai_family == AF_INET){ ret += (char)(prefixLen<=32 ? prefixLen + 96 : prefixLen); }else{ ret += (char)prefixLen; } } freeaddrinfo(result); return ret; } /// Checks bytes (length len) containing a binary-encoded IPv4 or IPv6 IP address, and writes it in human-readable notation to target. /// Writes "unknown" if it cannot decode to a sensible value. void Socket::hostBytesToStr(const char *bytes, size_t len, std::string &target){ switch (len){ case 4: char tmpstr[16]; snprintf(tmpstr, 16, "%hhu.%hhu.%hhu.%hhu", bytes[0], bytes[1], bytes[2], bytes[3]); target = tmpstr; break; case 16: if (memcmp(bytes, "\000\000\000\000\000\000\000\000\000\000\377\377", 12) == 0){ char tmpstr[16]; snprintf(tmpstr, 16, "%hhu.%hhu.%hhu.%hhu", bytes[12], bytes[13], bytes[14], bytes[15]); target = tmpstr; }else{ char tmpstr[40]; snprintf(tmpstr, 40, "%0.2x%0.2x:%0.2x%0.2x:%0.2x%0.2x:%0.2x%0.2x:%0.2x%0.2x:%0.2x%0.2x:%0.2x%0.2x:%0.2x%0.2x", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); target = tmpstr; } break; default: target = "unknown"; break; } } std::string uint2string(unsigned int i){ std::stringstream st; st << i; return st.str(); } Socket::Buffer::Buffer(){ splitter = "\n"; } /// Returns the amount of elements in the internal std::deque of std::string objects. /// The back is popped as long as it is empty, first - this way this function is /// guaranteed to return 0 if the buffer is empty. unsigned int Socket::Buffer::size(){ while (data.size() > 0 && data.back().empty()){data.pop_back();} return data.size(); } /// Returns either the amount of total bytes available in the buffer or max, whichever is smaller. unsigned int Socket::Buffer::bytes(unsigned int max){ unsigned int i = 0; for (std::deque::iterator it = data.begin(); it != data.end(); ++it){ i += (*it).size(); if (i >= max){return max;} } return i; } /// Returns how many bytes to read until the next splitter, or 0 if none found. unsigned int Socket::Buffer::bytesToSplit(){ unsigned int i = 0; for (std::deque::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ i += (*it).size(); if ((*it).size() >= splitter.size() && (*it).substr((*it).size()-splitter.size()) == splitter){ return i; } } return 0; } /// Appends this string to the internal std::deque of std::string objects. /// It is automatically split every BUFFER_BLOCKSIZE bytes and when the splitter string is encountered. void Socket::Buffer::append(const std::string &newdata){ append(newdata.data(), newdata.size()); } ///Helper function that does a short-circuiting string compare inline bool string_compare(const char *a, const char *b, const size_t len){ for (size_t i = 0; i < len; ++i){ if (a[i] != b[i]){return false;} } return true; } /// Appends this data block to the internal std::deque of std::string objects. /// It is automatically split every BUFFER_BLOCKSIZE bytes and when the splitter string is encountered. void Socket::Buffer::append(const char *newdata, const unsigned int newdatasize){ uint32_t i = 0; while (i < newdatasize){ uint32_t j = 0; if (!splitter.size()){ if (newdatasize - i > BUFFER_BLOCKSIZE){ j = BUFFER_BLOCKSIZE; }else{ j = newdatasize - i; } }else{ while (j+i < newdatasize && j < BUFFER_BLOCKSIZE){ j++; if (j >= splitter.size()){ if (string_compare(newdata+i+j-splitter.size(), splitter.data(), splitter.size())){break;} } } } if (j){ data.push_front(""); data.front().assign(newdata + i, (size_t)j); i += j; }else{ FAIL_MSG("Appended an empty string to buffer: aborting!"); break; } } if (data.size() > 5000){ DEBUG_MSG(DLVL_WARN, "Warning: After %d new bytes, buffer has %d parts containing over %u bytes!", newdatasize, (int)data.size(), bytes(9000)); } } /// Prepends this data block to the internal std::deque of std::string objects. /// It is _not_ automatically split every BUFFER_BLOCKSIZE bytes. void Socket::Buffer::prepend(const std::string &newdata){ data.push_back(newdata); } /// Prepends this data block to the internal std::deque of std::string objects. /// It is _not_ automatically split every BUFFER_BLOCKSIZE bytes. void Socket::Buffer::prepend(const char *newdata, const unsigned int newdatasize){ data.push_back(std::string(newdata, (size_t)newdatasize)); } /// Returns true if at least count bytes are available in this buffer. bool Socket::Buffer::available(unsigned int count){ size(); unsigned int i = 0; for (std::deque::iterator it = data.begin(); it != data.end(); ++it){ i += (*it).size(); if (i >= count){return true;} } return false; } /// Removes count bytes from the buffer, returning them by value. /// Returns an empty string if not all count bytes are available. std::string Socket::Buffer::remove(unsigned int count){ size(); if (!available(count)){return "";} unsigned int i = 0; std::string ret; ret.reserve(count); for (std::deque::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ if (i + (*it).size() < count){ ret.append(*it); i += (*it).size(); (*it).clear(); }else{ ret.append(*it, 0, count - i); (*it).erase(0, count - i); break; } } return ret; } /// Copies count bytes from the buffer, returning them by value. /// Returns an empty string if not all count bytes are available. std::string Socket::Buffer::copy(unsigned int count){ size(); if (!available(count)){return "";} unsigned int i = 0; std::string ret; ret.reserve(count); for (std::deque::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ if (i + (*it).size() < count){ ret.append(*it); i += (*it).size(); }else{ ret.append(*it, 0, count - i); break; } } return ret; } /// Gets a reference to the back of the internal std::deque of std::string objects. std::string &Socket::Buffer::get(){ size(); static std::string empty; if (data.size() > 0){ return data.back(); }else{ return empty; } } /// Completely empties the buffer void Socket::Buffer::clear(){ data.clear(); } /// Create a new base socket. This is a basic constructor for converting any valid socket to a Socket::Connection. /// \param sockNo Integer representing the socket to convert. Socket::Connection::Connection(int sockNo){ sock = sockNo; pipes[0] = -1; pipes[1] = -1; up = 0; down = 0; conntime = Util::epoch(); Error = false; Blocking = false; }// Socket::Connection basic constructor /// Simulate a socket using two file descriptors. /// \param write The filedescriptor to write to. /// \param read The filedescriptor to read from. Socket::Connection::Connection(int write, int read){ sock = -1; pipes[0] = write; pipes[1] = read; up = 0; down = 0; conntime = Util::epoch(); Error = false; Blocking = false; }// Socket::Connection basic constructor /// Create a new disconnected base socket. This is a basic constructor for placeholder purposes. /// A socket created like this is always disconnected and should/could be overwritten at some point. Socket::Connection::Connection(){ sock = -1; pipes[0] = -1; pipes[1] = -1; up = 0; down = 0; conntime = Util::epoch(); Error = false; Blocking = false; }// Socket::Connection basic constructor void Socket::Connection::resetCounter(){ up = 0; down = 0; } void Socket::Connection::addUp(const uint32_t i){ up += i; } void Socket::Connection::addDown(const uint32_t i){ down += i; } /// Internally used call to make an file descriptor blocking or not. void setFDBlocking(int FD, bool blocking){ int flags = fcntl(FD, F_GETFL, 0); if (!blocking){ flags |= O_NONBLOCK; }else{ flags &= !O_NONBLOCK; } fcntl(FD, F_SETFL, flags); } /// Internally used call to make an file descriptor blocking or not. bool isFDBlocking(int FD){ int flags = fcntl(FD, F_GETFL, 0); return !(flags & O_NONBLOCK); } /// Set this socket to be blocking (true) or nonblocking (false). void Socket::Connection::setBlocking(bool blocking){ if (sock >= 0){setFDBlocking(sock, blocking);} if (pipes[0] >= 0){setFDBlocking(pipes[0], blocking);} if (pipes[1] >= 0){setFDBlocking(pipes[1], blocking);} } /// Set this socket to be blocking (true) or nonblocking (false). bool Socket::Connection::isBlocking(){ if (sock >= 0){return isFDBlocking(sock);} if (pipes[0] >= 0){return isFDBlocking(pipes[0]);} if (pipes[1] >= 0){return isFDBlocking(pipes[1]);} return false; } /// Close connection. The internal socket is closed and then set to -1. /// If the connection is already closed, nothing happens. /// This function calls shutdown, thus making the socket unusable in all other /// processes as well. Do not use on shared sockets that are still in use. void Socket::Connection::close(){ if (sock != -1){shutdown(sock, SHUT_RDWR);} drop(); }// Socket::Connection::close /// Close connection. The internal socket is closed and then set to -1. /// If the connection is already closed, nothing happens. /// This function does *not* call shutdown, allowing continued use in other /// processes. void Socket::Connection::drop(){ if (connected()){ if (sock != -1){ DEBUG_MSG(DLVL_HIGH, "Socket %d closed", sock); errno = EINTR; while (::close(sock) != 0 && errno == EINTR){} sock = -1; } if (pipes[0] != -1){ errno = EINTR; while (::close(pipes[0]) != 0 && errno == EINTR){} pipes[0] = -1; } if (pipes[1] != -1){ errno = EINTR; while (::close(pipes[1]) != 0 && errno == EINTR){} pipes[1] = -1; } } }// Socket::Connection::drop /// Returns internal socket number. int Socket::Connection::getSocket(){ if (sock != -1){return sock;} if (pipes[0] != -1){return pipes[0];} if (pipes[1] != -1){return pipes[1];} return -1; } /// Returns non-piped internal socket number. int Socket::Connection::getPureSocket(){ return sock; } /// Returns a string describing the last error that occured. /// Only reports errors if an error actually occured - returns the host address or empty string otherwise. std::string Socket::Connection::getError(){ return remotehost; } /// Create a new Unix Socket. This socket will (try to) connect to the given address right away. /// \param address String containing the location of the Unix socket to connect to. /// \param nonblock Whether the socket should be nonblocking. False by default. Socket::Connection::Connection(std::string address, bool nonblock){ pipes[0] = -1; pipes[1] = -1; sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0){ remotehost = strerror(errno); DEBUG_MSG(DLVL_FAIL, "Could not create socket! Error: %s", remotehost.c_str()); return; } Error = false; Blocking = false; up = 0; down = 0; conntime = Util::epoch(); sockaddr_un addr; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, address.c_str(), address.size() + 1); int r = connect(sock, (sockaddr *)&addr, sizeof(addr)); if (r == 0){ if (nonblock){ int flags = fcntl(sock, F_GETFL, 0); flags |= O_NONBLOCK; fcntl(sock, F_SETFL, flags); } }else{ remotehost = strerror(errno); DEBUG_MSG(DLVL_FAIL, "Could not connect to %s! Error: %s", address.c_str(), remotehost.c_str()); close(); } }// Socket::Connection Unix Contructor /// Create a new TCP Socket. This socket will (try to) connect to the given host/port right away. /// \param host String containing the hostname to connect to. /// \param port String containing the port to connect to. /// \param nonblock Whether the socket should be nonblocking. Socket::Connection::Connection(std::string host, int port, bool nonblock){ pipes[0] = -1; pipes[1] = -1; struct addrinfo *result, *rp, hints; Error = false; Blocking = false; up = 0; down = 0; conntime = Util::epoch(); std::stringstream ss; ss << port; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG; int s = getaddrinfo(host.c_str(), ss.str().c_str(), &hints, &result); if (s != 0){ DEBUG_MSG(DLVL_FAIL, "Could not connect to %s:%i! Error: %s", host.c_str(), port, gai_strerror(s)); close(); return; } remotehost = ""; for (rp = result; rp != NULL; rp = rp->ai_next){ sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sock < 0){continue;} if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){break;} remotehost += strerror(errno); ::close(sock); } freeaddrinfo(result); if (rp == 0){ DEBUG_MSG(DLVL_FAIL, "Could not connect to %s! Error: %s", host.c_str(), remotehost.c_str()); close(); }else{ if (nonblock){ int flags = fcntl(sock, F_GETFL, 0); flags |= O_NONBLOCK; fcntl(sock, F_SETFL, flags); } } }// Socket::Connection TCP Contructor /// Returns the connected-state for this socket. /// Note that this function might be slightly behind the real situation. /// The connection status is updated after every read/write attempt, when errors occur /// and when the socket is closed manually. /// \returns True if socket is connected, false otherwise. bool Socket::Connection::connected() const{ return (sock >= 0) || ((pipes[0] >= 0) || (pipes[1] >= 0)); } /// Returns the time this socket has been connected. unsigned int Socket::Connection::connTime(){ return conntime; } /// Returns total amount of bytes sent. uint64_t Socket::Connection::dataUp(){ return up; } /// Returns total amount of bytes received. uint64_t Socket::Connection::dataDown(){ return down; } /// Returns a std::string of stats, ended by a newline. /// Requires the current connector name as an argument. std::string Socket::Connection::getStats(std::string C){ return "S " + getHost() + " " + C + " " + uint2string(Util::epoch() - conntime) + " " + uint2string(up) + " " + uint2string(down) + "\n"; } /// Updates the downbuffer internal variable. /// Returns true if new data was received, false otherwise. bool Socket::Connection::spool(){ /// \todo Provide better mechanism to prevent overbuffering. if (downbuffer.size() > 10000){ return true; }else{ return iread(downbuffer); } } bool Socket::Connection::peek(){ /// clear buffer downbuffer.clear(); return iread(downbuffer, MSG_PEEK); } /// Returns a reference to the download buffer. Socket::Buffer &Socket::Connection::Received(){ return downbuffer; } /// Will not buffer anything but always send right away. Blocks. /// Any data that could not be send will block until it can be send or the connection is severed. void Socket::Connection::SendNow(const char *data, size_t len){ bool bing = isBlocking(); if (!bing){setBlocking(true);} unsigned int i = iwrite(data, std::min((long unsigned int)len, SOCKETSIZE)); while (i < len && connected()){i += iwrite(data + i, std::min((long unsigned int)(len - i), SOCKETSIZE));} if (!bing){setBlocking(false);} } /// Will not buffer anything but always send right away. Blocks. /// Any data that could not be send will block until it can be send or the connection is severed. void Socket::Connection::SendNow(const char *data){ int len = strlen(data); SendNow(data, len); } /// Will not buffer anything but always send right away. Blocks. /// Any data that could not be send will block until it can be send or the connection is severed. void Socket::Connection::SendNow(const std::string &data){ SendNow(data.data(), data.size()); } /// Incremental write call. This function tries to write len bytes to the socket from the buffer, /// returning the amount of bytes it actually wrote. /// \param buffer Location of the buffer to write from. /// \param len Amount of bytes to write. /// \returns The amount of bytes actually written. unsigned int Socket::Connection::iwrite(const void *buffer, int len){ if (!connected() || len < 1){return 0;} int r; if (sock >= 0){ r = send(sock, buffer, len, 0); }else{ r = write(pipes[0], buffer, len); } if (r < 0){ switch (errno){ case EWOULDBLOCK: return 0; break; default: Error = true; INSANE_MSG("Could not iwrite data! Error: %s", strerror(errno)); close(); return 0; break; } } if (r == 0 && (sock >= 0)){ DONTEVEN_MSG("Socket closed by remote"); close(); } up += r; return r; }// Socket::Connection::iwrite /// Incremental read call. This function tries to read len bytes to the buffer from the socket, /// returning the amount of bytes it actually read. /// \param buffer Location of the buffer to read to. /// \param len Amount of bytes to read. /// \param flags Flags to use in the recv call. Ignored on fake sockets. /// \returns The amount of bytes actually read. int Socket::Connection::iread(void *buffer, int len, int flags){ if (!connected() || len < 1){return 0;} int r; if (sock >= 0){ r = recv(sock, buffer, len, flags); }else{ r = recv(pipes[1], buffer, len, flags); if (r < 0 && errno == ENOTSOCK){r = read(pipes[1], buffer, len);} } if (r < 0){ switch (errno){ case EWOULDBLOCK: return 0; break; case EINTR: return 0; break; default: Error = true; INSANE_MSG("Could not iread data! Error: %s", strerror(errno)); close(); return 0; break; } } if (r == 0){ DONTEVEN_MSG("Socket closed by remote"); close(); } down += r; return r; }// Socket::Connection::iread /// Read call that is compatible with Socket::Buffer. /// Data is read using iread (which is nonblocking if the Socket::Connection itself is), /// then appended to end of buffer. /// \param buffer Socket::Buffer to append data to. /// \param flags Flags to use in the recv call. Ignored on fake sockets. /// \return True if new data arrived, false otherwise. bool Socket::Connection::iread(Buffer &buffer, int flags){ char cbuffer[BUFFER_BLOCKSIZE]; int num = iread(cbuffer, BUFFER_BLOCKSIZE, flags); if (num < 1){return false;} buffer.append(cbuffer, num); return true; }// iread /// Incremental write call that is compatible with std::string. /// Data is written using iwrite (which is nonblocking if the Socket::Connection itself is), /// then removed from front of buffer. /// \param buffer std::string to remove data from. /// \return True if more data was sent, false otherwise. bool Socket::Connection::iwrite(std::string &buffer){ if (buffer.size() < 1){return false;} unsigned int tmp = iwrite((void *)buffer.c_str(), buffer.size()); if (!tmp){return false;} buffer = buffer.substr(tmp); return true; }// iwrite /// Gets hostname for connection, if available. std::string Socket::Connection::getHost() const{ return remotehost; } /// Gets binary IPv6 address for connection, if available. /// Guaranteed to be either empty or 16 bytes long. std::string Socket::Connection::getBinHost(){ return getIPv6BinAddr(remoteaddr); } /// Sets hostname for connection manually. /// Overwrites the detected host, thus possibily making it incorrect. void Socket::Connection::setHost(std::string host){ remotehost = host; struct addrinfo *result, *rp, hints; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED; hints.ai_protocol = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; int s = getaddrinfo(host.c_str(), 0, &hints, &result); if (s != 0){return;} if (result){ remoteaddr = *((sockaddr_in6 *)result->ai_addr); } freeaddrinfo(result); } /// Returns true if these sockets are the same socket. /// Does not check the internal stats - only the socket itself. bool Socket::Connection::operator==(const Connection &B) const{ return sock == B.sock && pipes[0] == B.pipes[0] && pipes[1] == B.pipes[1]; } /// Returns true if these sockets are not the same socket. /// Does not check the internal stats - only the socket itself. bool Socket::Connection::operator!=(const Connection &B) const{ return sock != B.sock || pipes[0] != B.pipes[0] || pipes[1] != B.pipes[1]; } /// Returns true if the socket is valid. /// Aliases for Socket::Connection::connected() Socket::Connection::operator bool() const{ return connected(); } /// Returns true if the given address can be matched with the remote host. /// Can no longer return true after any socket error have occurred. bool Socket::Connection::isAddress(const std::string &addr){ //Retrieve current socket binary address std::string myBinAddr = getBinHost(); return isBinAddress(myBinAddr, addr); } bool Socket::Connection::isLocal(){ struct ifaddrs * ifAddrStruct=NULL; struct ifaddrs * ifa=NULL; void * tmpAddrPtr=NULL; char addressBuffer[INET6_ADDRSTRLEN]; getifaddrs(&ifAddrStruct); for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next){ if (!ifa->ifa_addr){ continue; } if (ifa->ifa_addr->sa_family == AF_INET){// check it is IP4 tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); INSANE_MSG("Comparing '%s' to '%s'", remotehost.c_str(), addressBuffer); if (remotehost == addressBuffer){return true;} INSANE_MSG("Comparing '%s' to '::ffff:%s'", remotehost.c_str(), addressBuffer); if (remotehost == std::string("::ffff:") + addressBuffer){return true;} }else if (ifa->ifa_addr->sa_family == AF_INET6){// check it is IP6 tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); INSANE_MSG("Comparing '%s' to '%s'", remotehost.c_str(), addressBuffer); if (remotehost == addressBuffer){return true;} } } if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct); return false; } /// Create a new base Server. The socket is never connected, and a placeholder for later connections. Socket::Server::Server(){ sock = -1; }// Socket::Server base Constructor /// Create a new TCP Server. The socket is immediately bound and set to listen. /// A maximum of 100 connections will be accepted between accept() calls. /// Any further connections coming in will be dropped. /// \param port The TCP port to listen on /// \param hostname (optional) The interface to bind to. The default is 0.0.0.0 (all interfaces). /// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). Socket::Server::Server(int port, std::string hostname, bool nonblock){ if (!IPv6bind(port, hostname, nonblock) && !IPv4bind(port, hostname, nonblock)){ DEBUG_MSG(DLVL_FAIL, "Could not create socket %s:%i! Error: %s", hostname.c_str(), port, errors.c_str()); sock = -1; } }// Socket::Server TCP Constructor /// Attempt to bind an IPv6 socket. /// \param port The TCP port to listen on /// \param hostname The interface to bind to. The default is 0.0.0.0 (all interfaces). /// \param nonblock Whether accept() calls will be nonblocking. Default is false (blocking). /// \return True if successful, false otherwise. bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0){ errors = strerror(errno); DEBUG_MSG(DLVL_ERROR, "Could not create IPv6 socket %s:%i! Error: %s", hostname.c_str(), port, errors.c_str()); return false; } int on = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); #ifdef __CYGWIN__ on = 0; setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); #endif if (nonblock){ int flags = fcntl(sock, F_GETFL, 0); flags |= O_NONBLOCK; fcntl(sock, F_SETFL, flags); } struct sockaddr_in6 addr; memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(port); // set port if (hostname == "0.0.0.0" || hostname.length() == 0){ addr.sin6_addr = in6addr_any; }else{ inet_pton(AF_INET6, hostname.c_str(), &addr.sin6_addr); // set interface, 0.0.0.0 (default) is all } int ret = bind(sock, (sockaddr *)&addr, sizeof(addr)); // do the actual bind if (ret == 0){ ret = listen(sock, 100); // start listening, backlog of 100 allowed if (ret == 0){ DEBUG_MSG(DLVL_DEVEL, "IPv6 socket success @ %s:%i", hostname.c_str(), port); return true; }else{ errors = strerror(errno); DEBUG_MSG(DLVL_ERROR, "IPv6 listen failed! Error: %s", errors.c_str()); close(); return false; } }else{ errors = strerror(errno); DEBUG_MSG(DLVL_ERROR, "IPv6 Binding %s:%i failed (%s)", hostname.c_str(), port, errors.c_str()); close(); return false; } } /// Attempt to bind an IPv4 socket. /// \param port The TCP port to listen on /// \param hostname The interface to bind to. The default is 0.0.0.0 (all interfaces). /// \param nonblock Whether accept() calls will be nonblocking. Default is false (blocking). /// \return True if successful, false otherwise. bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0){ errors = strerror(errno); DEBUG_MSG(DLVL_ERROR, "Could not create IPv4 socket %s:%i! Error: %s", hostname.c_str(), port, errors.c_str()); return false; } int on = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (nonblock){ int flags = fcntl(sock, F_GETFL, 0); flags |= O_NONBLOCK; fcntl(sock, F_SETFL, flags); } struct sockaddr_in addr4; memset(&addr4, 0, sizeof(addr4)); addr4.sin_family = AF_INET; addr4.sin_port = htons(port); // set port if (hostname == "0.0.0.0" || hostname.length() == 0){ addr4.sin_addr.s_addr = INADDR_ANY; }else{ inet_pton(AF_INET, hostname.c_str(), &addr4.sin_addr); // set interface, 0.0.0.0 (default) is all } int ret = bind(sock, (sockaddr *)&addr4, sizeof(addr4)); // do the actual bind if (ret == 0){ ret = listen(sock, 100); // start listening, backlog of 100 allowed if (ret == 0){ DEBUG_MSG(DLVL_DEVEL, "IPv4 socket success @ %s:%i", hostname.c_str(), port); return true; }else{ errors = strerror(errno); DEBUG_MSG(DLVL_ERROR, "IPv4 listen failed! Error: %s", errors.c_str()); close(); return false; } }else{ errors = strerror(errno); DEBUG_MSG(DLVL_ERROR, "IPv4 Binding %s:%i failed (%s)", hostname.c_str(), port, errors.c_str()); close(); return false; } } /// Create a new Unix Server. The socket is immediately bound and set to listen. /// A maximum of 100 connections will be accepted between accept() calls. /// Any further connections coming in will be dropped. /// The address used will first be unlinked - so it succeeds if the Unix socket already existed. Watch out for this behaviour - it will delete /// any file located at address! /// \param address The location of the Unix socket to bind to. /// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). Socket::Server::Server(std::string address, bool nonblock){ unlink(address.c_str()); sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0){ errors = strerror(errno); DEBUG_MSG(DLVL_ERROR, "Could not create unix socket %s! Error: %s", address.c_str(), errors.c_str()); return; } if (nonblock){ int flags = fcntl(sock, F_GETFL, 0); flags |= O_NONBLOCK; fcntl(sock, F_SETFL, flags); } sockaddr_un addr; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, address.c_str(), address.size() + 1); int ret = bind(sock, (sockaddr *)&addr, sizeof(addr)); if (ret == 0){ ret = listen(sock, 100); // start listening, backlog of 100 allowed if (ret == 0){ return; }else{ errors = strerror(errno); DEBUG_MSG(DLVL_ERROR, "Unix listen failed! Error: %s", errors.c_str()); close(); return; } }else{ errors = strerror(errno); DEBUG_MSG(DLVL_ERROR, "Unix Binding %s failed (%s)", address.c_str(), errors.c_str()); close(); return; } }// Socket::Server Unix Constructor /// Accept any waiting connections. If the Socket::Server is blocking, this function will block until there is an incoming connection. /// If the Socket::Server is nonblocking, it might return a Socket::Connection that is not connected, so check for this. /// \param nonblock (optional) Whether the newly connected socket should be nonblocking. Default is false (blocking). /// \returns A Socket::Connection, which may or may not be connected, depending on settings and circumstances. Socket::Connection Socket::Server::accept(bool nonblock){ if (sock < 0){return Socket::Connection(-1);} struct sockaddr_in6 tmpaddr; socklen_t len = sizeof(tmpaddr); static char addrconv[INET6_ADDRSTRLEN]; int r = ::accept(sock, (sockaddr *)&tmpaddr, &len); // set the socket to be nonblocking, if requested. // we could do this through accept4 with a flag, but that call is non-standard... if ((r >= 0) && nonblock){ int flags = fcntl(r, F_GETFL, 0); flags |= O_NONBLOCK; fcntl(r, F_SETFL, flags); } Socket::Connection tmp(r); tmp.remoteaddr = tmpaddr; if (r < 0){ if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EINTR)){ DEBUG_MSG(DLVL_FAIL, "Error during accept - closing server socket %d.", sock); close(); } }else{ if (tmpaddr.sin6_family == AF_INET6){ tmp.remotehost = inet_ntop(AF_INET6, &(tmpaddr.sin6_addr), addrconv, INET6_ADDRSTRLEN); DEBUG_MSG(DLVL_HIGH, "IPv6 addr [%s]", tmp.remotehost.c_str()); } if (tmpaddr.sin6_family == AF_INET){ tmp.remotehost = inet_ntop(AF_INET, &(((sockaddr_in *)&tmpaddr)->sin_addr), addrconv, INET6_ADDRSTRLEN); DEBUG_MSG(DLVL_HIGH, "IPv4 addr [%s]", tmp.remotehost.c_str()); } if (tmpaddr.sin6_family == AF_UNIX){ DEBUG_MSG(DLVL_HIGH, "Unix connection"); tmp.remotehost = "UNIX_SOCKET"; } } return tmp; } /// Set this socket to be blocking (true) or nonblocking (false). void Socket::Server::setBlocking(bool blocking){ if (sock >= 0){setFDBlocking(sock, blocking);} } /// Set this socket to be blocking (true) or nonblocking (false). bool Socket::Server::isBlocking(){ if (sock >= 0){return isFDBlocking(sock);} return false; } /// Close connection. The internal socket is closed and then set to -1. /// If the connection is already closed, nothing happens. /// This function calls shutdown, thus making the socket unusable in all other /// processes as well. Do not use on shared sockets that are still in use. void Socket::Server::close(){ if (sock != -1){shutdown(sock, SHUT_RDWR);} drop(); }// Socket::Server::close /// Close connection. The internal socket is closed and then set to -1. /// If the connection is already closed, nothing happens. /// This function does *not* call shutdown, allowing continued use in other /// processes. void Socket::Server::drop(){ if (connected()){ if (sock != -1){ DEBUG_MSG(DLVL_HIGH, "ServerSocket %d closed", sock); errno = EINTR; while (::close(sock) != 0 && errno == EINTR){} sock = -1; } } }// Socket::Server::drop /// Returns the connected-state for this socket. /// Note that this function might be slightly behind the real situation. /// The connection status is updated after every accept attempt, when errors occur /// and when the socket is closed manually. /// \returns True if socket is connected, false otherwise. bool Socket::Server::connected() const{ return (sock >= 0); }// Socket::Server::connected /// Returns internal socket number. int Socket::Server::getSocket(){ return sock; } /// Create a new UDP Socket. /// Will attempt to create an IPv6 UDP socket, on fail try a IPV4 UDP socket. /// If both fail, prints an DLVL_FAIL debug message. /// \param nonblock Whether the socket should be nonblocking. Socket::UDPConnection::UDPConnection(bool nonblock){ family = AF_INET6; sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock == -1){ sock = socket(AF_INET, SOCK_DGRAM, 0); family = AF_INET; } if (sock == -1){DEBUG_MSG(DLVL_FAIL, "Could not create UDP socket: %s", strerror(errno));} up = 0; down = 0; destAddr = 0; destAddr_size = 0; data = 0; data_size = 0; data_len = 0; if (nonblock){setBlocking(!nonblock);} }// Socket::UDPConnection UDP Contructor /// Copies a UDP socket, re-allocating local copies of any needed structures. /// The data/data_size/data_len variables are *not* copied over. Socket::UDPConnection::UDPConnection(const UDPConnection &o){ family = AF_INET6; sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock == -1){ sock = socket(AF_INET, SOCK_DGRAM, 0); family = AF_INET; } if (sock == -1){DEBUG_MSG(DLVL_FAIL, "Could not create UDP socket: %s", strerror(errno));} up = 0; down = 0; if (o.destAddr && o.destAddr_size){ destAddr = malloc(o.destAddr_size); destAddr_size = o.destAddr_size; if (destAddr){memcpy(destAddr, o.destAddr, o.destAddr_size);} }else{ destAddr = 0; destAddr_size = 0; } data = (char *)malloc(1024); if (data){ data_size = 1024; }else{ data_size = 0; } data_len = 0; } /// Close the UDP socket void Socket::UDPConnection::close(){ if (sock != -1){ errno = EINTR; while (::close(sock) != 0 && errno == EINTR){} sock = -1; } } /// Closes the UDP socket, cleans up any memory allocated by the socket. Socket::UDPConnection::~UDPConnection(){ close(); if (destAddr){ free(destAddr); destAddr = 0; } if (data){ free(data); data = 0; } } /// Stores the properties of the receiving end of this UDP socket. /// This will be the receiving end for all SendNow calls. void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port){ //UDP sockets can switch between IPv4 and IPv6 on demand. //We change IPv4-mapped IPv6 addresses into IPv4 addresses for Windows-sillyness reasons. if (destIp.substr(0, 7) == "::ffff:"){ destIp = destIp.substr(7); } struct addrinfo *result, *rp, hints; std::stringstream ss; ss << port; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED; hints.ai_protocol = IPPROTO_UDP; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; int s = getaddrinfo(destIp.c_str(), ss.str().c_str(), &hints, &result); if (s != 0){ DEBUG_MSG(DLVL_FAIL, "Could not connect UDP socket to %s:%i! Error: %s", destIp.c_str(), port, gai_strerror(s)); return; } for (rp = result; rp != NULL; rp = rp->ai_next){ // assume success if (destAddr){ free(destAddr); destAddr = 0; } destAddr_size = rp->ai_addrlen; destAddr = malloc(destAddr_size); if (!destAddr){return;} memcpy(destAddr, rp->ai_addr, rp->ai_addrlen); close(); family = rp->ai_family; sock = socket(family, SOCK_DGRAM, 0); HIGH_MSG("Set UDP destination: %s:%d (%s)", destIp.c_str(), port, addrFam(family)); freeaddrinfo(result); return; //\todo Possibly detect and handle failure } freeaddrinfo(result); free(destAddr); destAddr = 0; DEBUG_MSG(DLVL_FAIL, "Could not set destination for UDP socket: %s:%d", destIp.c_str(), port); }// Socket::UDPConnection SetDestination /// Gets the properties of the receiving end of this UDP socket. /// This will be the receiving end for all SendNow calls. void Socket::UDPConnection::GetDestination(std::string &destIp, uint32_t &port){ if (!destAddr || !destAddr_size){ destIp = ""; port = 0; return; } char addr_str[INET6_ADDRSTRLEN + 1]; addr_str[INET6_ADDRSTRLEN] = 0; // set last byte to zero, to prevent walking out of the array if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET6){ if (inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)destAddr)->sin6_addr), addr_str, INET6_ADDRSTRLEN) != 0){ destIp = addr_str; port = ntohs(((struct sockaddr_in6 *)destAddr)->sin6_port); return; } } if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET){ if (inet_ntop(AF_INET, &(((struct sockaddr_in *)destAddr)->sin_addr), addr_str, INET6_ADDRSTRLEN) != 0){ destIp = addr_str; port = ntohs(((struct sockaddr_in *)destAddr)->sin_port); return; } } destIp = ""; port = 0; DEBUG_MSG(DLVL_FAIL, "Could not get destination for UDP socket"); }// Socket::UDPConnection GetDestination /// Returns the port number of the receiving end of this socket. /// Returns 0 on error. uint32_t Socket::UDPConnection::getDestPort() const{ if (!destAddr || !destAddr_size){return 0;} if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET6){return ntohs(((struct sockaddr_in6 *)destAddr)->sin6_port);} if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET){return ntohs(((struct sockaddr_in *)destAddr)->sin_port);} return 0; } /// Sets the socket to be blocking if the parameters is true. /// Sets the socket to be non-blocking otherwise. void Socket::UDPConnection::setBlocking(bool blocking){ if (sock >= 0){setFDBlocking(sock, blocking);} } /// Sends a UDP datagram using the buffer sdata. /// This function simply calls SendNow(const char*, size_t) void Socket::UDPConnection::SendNow(const std::string &sdata){ SendNow(sdata.c_str(), sdata.size()); } /// Sends a UDP datagram using the buffer sdata. /// sdata is required to be NULL-terminated. /// This function simply calls SendNow(const char*, size_t) void Socket::UDPConnection::SendNow(const char *sdata){ int len = strlen(sdata); SendNow(sdata, len); } /// Sends a UDP datagram using the buffer sdata of length len. /// Does not do anything if len < 1. /// Prints an DLVL_FAIL level debug message if sending failed. void Socket::UDPConnection::SendNow(const char *sdata, size_t len){ if (len < 1){return;} int r = sendto(sock, sdata, len, 0, (sockaddr *)destAddr, destAddr_size); if (r > 0){ up += r; }else{ DEBUG_MSG(DLVL_FAIL, "Could not send UDP data through %d: %s", sock, strerror(errno)); } } /// Bind to a port number, returning the bound port. /// If that fails, returns zero. /// \arg port Port to bind to, required. /// \arg iface Interface address to listen for packets on (may be multicast address) /// \arg multicastInterfaces Comma-separated list of interfaces to listen on for multicast packets. Optional, left out means automatically chosen /// by kernel. /// \return Actually bound port number, or zero on error. uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::string &multicastInterfaces){ close(); // we open a new socket for each attempt int result = 0; int addr_ret; bool multicast = false; struct addrinfo hints, *addr_result, *rp; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE | AI_V4MAPPED; if (destAddr && destAddr_size){ hints.ai_family = ((struct sockaddr_in *)destAddr)->sin_family; }else{ hints.ai_family = AF_UNSPEC; } hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; std::stringstream ss; ss << port; if (iface == "0.0.0.0" || iface.length() == 0){ if ((addr_ret = getaddrinfo(0, ss.str().c_str(), &hints, &addr_result)) != 0){ FAIL_MSG("Could not resolve %s for UDP: %s", iface.c_str(), gai_strerror(addr_ret)); return 0; } }else{ if ((addr_ret = getaddrinfo(iface.c_str(), ss.str().c_str(), &hints, &addr_result)) != 0){ FAIL_MSG("Could not resolve %s for UDP: %s", iface.c_str(), gai_strerror(addr_ret)); return 0; } } std::string err_str; for (rp = addr_result; rp != NULL; rp = rp->ai_next){ sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sock == -1){continue;} char human_addr[INET6_ADDRSTRLEN]; char human_port[16]; getnameinfo(rp->ai_addr, rp->ai_addrlen, human_addr, INET6_ADDRSTRLEN, human_port, 16, NI_NUMERICHOST | NI_NUMERICSERV); MEDIUM_MSG("Attempting bind to %s:%s (%s)", human_addr, human_port, addrFam(rp->ai_family)); family = rp->ai_family; hints.ai_family = family; if (family == AF_INET6){ sockaddr_in6 *addr6 = (sockaddr_in6 *)(rp->ai_addr); result = ntohs(addr6->sin6_port); if (memcmp((char *)&(addr6->sin6_addr), "\000\000\000\000\000\000\000\000\000\000\377\377", 12) == 0){ // IPv6-mapped IPv4 address - 13th byte ([12]) holds the first IPv4 byte multicast = (((char *)&(addr6->sin6_addr))[12] & 0xF0) == 0xE0; }else{ //"normal" IPv6 address - prefix ff00::/8 multicast = (((char *)&(addr6->sin6_addr))[0] == 0xFF); } }else{ sockaddr_in *addr4 = (sockaddr_in *)(rp->ai_addr); result = ntohs(addr4->sin_port); // multicast has a "1110" bit prefix multicast = (((char *)&(addr4->sin_addr))[0] & 0xF0) == 0xE0; } if (multicast){ const int optval = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0){ WARN_MSG("Could not set multicast UDP socket re-use!"); } } if (::bind(sock, rp->ai_addr, rp->ai_addrlen) == 0){ INFO_MSG("UDP bind success on %s:%s (%s)", human_addr, human_port, addrFam(rp->ai_family)); break; } if (err_str.size()){err_str += ", ";} err_str += human_addr; err_str += ":"; err_str += strerror(errno); close(); // we open a new socket for each attempt } freeaddrinfo(addr_result); if (sock == -1){ FAIL_MSG("Could not open %s for UDP: %s", iface.c_str(), err_str.c_str()); return 0; } // handle multicast membership(s) if (multicast){ struct ipv6_mreq mreq6; struct ip_mreq mreq4; memset(&mreq4, 0, sizeof(mreq4)); memset(&mreq6, 0, sizeof(mreq6)); struct addrinfo *reslocal, *resmulti; if ((addr_ret = getaddrinfo(iface.c_str(), 0, &hints, &resmulti)) != 0){ WARN_MSG("Unable to parse multicast address: %s", gai_strerror(addr_ret)); close(); return 0; } if (!multicastInterfaces.length()){ if (family == AF_INET6){ memcpy(&mreq6.ipv6mr_multiaddr, &((sockaddr_in6 *)resmulti->ai_addr)->sin6_addr, sizeof(mreq6.ipv6mr_multiaddr)); if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq6, sizeof(mreq6)) != 0){ FAIL_MSG("Unable to register for IPv6 multicast on all interfaces: %s", strerror(errno)); close(); result = -1; } }else{ mreq4.imr_multiaddr = ((sockaddr_in *)resmulti->ai_addr)->sin_addr; mreq4.imr_interface.s_addr = INADDR_ANY; if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq4, sizeof(mreq4)) != 0){ FAIL_MSG("Unable to register for IPv4 multicast on all interfaces: %s", strerror(errno)); close(); result = -1; } } }else{ size_t nxtPos = std::string::npos; bool atLeastOne = false; for (size_t loc = 0; loc != std::string::npos; loc = (nxtPos == std::string::npos ? nxtPos : nxtPos + 1)){ nxtPos = multicastInterfaces.find(',', loc); std::string curIface = multicastInterfaces.substr(loc, (nxtPos == std::string::npos ? nxtPos : nxtPos - loc)); // do a bit of filtering for IPv6, removing the []-braces, if any if (curIface[0] == '['){ if (curIface[curIface.size() - 1] == ']'){ curIface = curIface.substr(1, curIface.size() - 2); }else{ curIface = curIface.substr(1, curIface.size() - 1); } } if (family == AF_INET6){ INFO_MSG("Registering for IPv6 multicast on interface %s", curIface.c_str()); if ((addr_ret = getaddrinfo(curIface.c_str(), 0, &hints, &reslocal)) != 0){ WARN_MSG("Unable to resolve IPv6 interface address %s: %s", curIface.c_str(), gai_strerror(addr_ret)); continue; } memcpy(&mreq6.ipv6mr_multiaddr, &((sockaddr_in6 *)resmulti->ai_addr)->sin6_addr, sizeof(mreq6.ipv6mr_multiaddr)); mreq6.ipv6mr_interface = ((sockaddr_in6 *)reslocal->ai_addr)->sin6_scope_id; if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq6, sizeof(mreq6)) != 0){ FAIL_MSG("Unable to register for IPv6 multicast on interface %s (%u): %s", curIface.c_str(), ((sockaddr_in6 *)reslocal->ai_addr)->sin6_scope_id, strerror(errno)); }else{ atLeastOne = true; } }else{ INFO_MSG("Registering for IPv4 multicast on interface %s", curIface.c_str()); if ((addr_ret = getaddrinfo(curIface.c_str(), 0, &hints, &reslocal)) != 0){ WARN_MSG("Unable to resolve IPv4 interface address %s: %s", curIface.c_str(), gai_strerror(addr_ret)); continue; } mreq4.imr_multiaddr = ((sockaddr_in *)resmulti->ai_addr)->sin_addr; mreq4.imr_interface = ((sockaddr_in *)reslocal->ai_addr)->sin_addr; if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq4, sizeof(mreq4)) != 0){ FAIL_MSG("Unable to register for IPv4 multicast on interface %s: %s", curIface.c_str(), strerror(errno)); }else{ atLeastOne = true; } } if (!atLeastOne){ close(); result = -1; } freeaddrinfo(reslocal); // free resolved interface addr }// loop over all interfaces } freeaddrinfo(resmulti); // free resolved multicast addr } //get port number struct sockaddr_storage fin_addr; socklen_t alen = sizeof(fin_addr); if (getsockname(sock, (struct sockaddr*)&fin_addr, &alen) == 0){ if (family == AF_INET6){ return ntohs(((struct sockaddr_in6*)&fin_addr)->sin6_port); }else{ return ntohs(((struct sockaddr_in*)&fin_addr)->sin_port); } }else{ return 0; } } /// Attempt to receive a UDP packet. /// This will automatically allocate or resize the internal data buffer if needed. /// If a packet is received, it will be placed in the "data" member, with it's length in "data_len". /// \return True if a packet was received, false otherwise. bool Socket::UDPConnection::Receive(){ #ifdef __CYGWIN__ if (data_size != SOCKETSIZE){ data = (char *)realloc(data, SOCKETSIZE); data_size = SOCKETSIZE; } #endif int r = recvfrom(sock, data, data_size, MSG_PEEK | MSG_TRUNC | MSG_DONTWAIT, 0, 0); if (r == -1){ if (errno != EAGAIN){INFO_MSG("UDP receive: %d (%s)", errno, strerror(errno));} data_len = 0; return false; } if (data_size < (unsigned int)r){ char *tmp = (char *)realloc(data, r); if (tmp){ data = tmp; data_size = r; }else{ FAIL_MSG("Could not resize socket buffer to %d bytes!", r); return false; } } socklen_t destsize = destAddr_size; r = recvfrom(sock, data, data_size, 0, (sockaddr *)destAddr, &destsize); if (r > 0){ down += r; data_len = r; return true; }else{ data_len = 0; return false; } } int Socket::UDPConnection::getSock(){ return sock; }