/// \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); } bool Socket::isLocalhost(const std::string &remotehost){ std::string tmpInput = remotehost; std::string bf = Socket::getBinForms(tmpInput); std::string tmpAddr; while (bf.size() >= 16){ Socket::hostBytesToStr(bf.data(), 16, tmpAddr); if (isLocal(tmpAddr)){return true;} bf.erase(0, 17); } return false; } bool Socket::isLocal(const std::string &remotehost){ 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; } /// 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, "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.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){ WARN_MSG("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){ sSend = sockNo; sRecv = -1; isTrueSocket = false; struct stat sBuf; if (sSend != -1 && !fstat(sSend, &sBuf)){isTrueSocket = S_ISSOCK(sBuf.st_mode);} up = 0; down = 0; conntime = Util::epoch(); Error = false; Blocking = false; skipCount = 0; }// 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){ sSend = write; if (write != read){ sRecv = read; }else{ sRecv = -1; } isTrueSocket = false; struct stat sBuf; if (sSend != -1 && !fstat(sSend, &sBuf)){isTrueSocket = S_ISSOCK(sBuf.st_mode);} up = 0; down = 0; conntime = Util::epoch(); Error = false; Blocking = false; skipCount = 0; }// 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(){ sSend = -1; sRecv = -1; isTrueSocket = false; up = 0; down = 0; conntime = Util::epoch(); Error = false; Blocking = false; skipCount = 0; }// 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 (sSend >= 0){setFDBlocking(sSend, blocking);} if (sRecv >= 0 && sSend != sRecv){setFDBlocking(sRecv, blocking);} } /// Set this socket to be blocking (true) or nonblocking (false). bool Socket::Connection::isBlocking(){ if (sSend >= 0){return isFDBlocking(sSend);} if (sRecv >= 0){return isFDBlocking(sRecv);} 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 (sSend != -1){shutdown(sSend, 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 (sSend != -1){ HIGH_MSG("Socket %d closed", sSend); errno = EINTR; while (::close(sSend) != 0 && errno == EINTR){} if (sRecv == sSend){sRecv = -1;} sSend = -1; } if (sRecv != -1){ errno = EINTR; while (::close(sRecv) != 0 && errno == EINTR){} sRecv = -1; } } }// Socket::Connection::drop /// Returns internal socket number. int Socket::Connection::getSocket(){ if (sSend != -1){return sSend;} return sRecv; } /// Returns non-piped internal socket number. int Socket::Connection::getPureSocket(){ if (!isTrueSocket){return -1;} return sSend; } /// 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){ skipCount = 0; sRecv = -1; isTrueSocket = true; sSend = socket(PF_UNIX, SOCK_STREAM, 0); if (sSend < 0){ remotehost = strerror(errno); FAIL_MSG("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(sSend, (sockaddr *)&addr, sizeof(addr)); if (r == 0){ if (nonblock){ int flags = fcntl(sSend, F_GETFL, 0); flags |= O_NONBLOCK; fcntl(sSend, F_SETFL, flags); } }else{ remotehost = strerror(errno); FAIL_MSG("Could not connect to %s! Error: %s", address.c_str(), remotehost.c_str()); close(); } }// Socket::Connection Unix Constructor /// 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){ skipCount = 0; sRecv = -1; isTrueSocket = true; 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){ FAIL_MSG("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){ sSend = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sSend < 0){continue;} if (connect(sSend, rp->ai_addr, rp->ai_addrlen) == 0){break;} remotehost += strerror(errno); ::close(sSend); } freeaddrinfo(result); if (rp == 0){ FAIL_MSG("Could not connect to %s! Error: %s", host.c_str(), remotehost.c_str()); close(); }else{ if (nonblock){ int flags = fcntl(sSend, F_GETFL, 0); flags |= O_NONBLOCK; fcntl(sSend, F_SETFL, flags); } int optval = 1; int optlen = sizeof(optval); setsockopt(sSend, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); } }// Socket::Connection TCP Constructor /// 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 (sSend >= 0) || (sRecv >= 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()); } void Socket::Connection::skipBytes(uint32_t byteCount){ INFO_MSG("Skipping first %" PRIu32 " bytes going to socket", byteCount); skipCount = byteCount; } /// 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;} if (skipCount){ // We have bytes to skip writing. // Pretend we write them, but don't really. if (len <= skipCount){ skipCount -= len; return len; }else{ unsigned int toSkip = skipCount; skipCount = 0; return iwrite((((char *)buffer) + toSkip), len - toSkip) + toSkip; } } int r; if (isTrueSocket){ r = send(sSend, buffer, len, 0); }else{ r = write(sSend, 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 && (sSend >= 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 (sRecv != -1 || !isTrueSocket){ r = read(sRecv, buffer, len); }else{ r = recv(sSend, buffer, len, flags); } 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, 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 sSend == B.sSend && sRecv == B.sRecv; } /// 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 sSend != B.sSend || sRecv != B.sRecv; } /// 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(){ return Socket::isLocal(remotehost); } #ifdef SSL Socket::SSLConnection::SSLConnection() : Socket::Connection::Connection(){ isConnected = false; server_fd = 0; ssl = 0; conf = 0; ctr_drbg = 0; entropy = 0; } static void my_debug(void *ctx, int level, const char *file, int line, const char *str){ ((void)level); fprintf((FILE *)ctx, "%s:%04d: %s", file, line, str); fflush((FILE *)ctx); } Socket::SSLConnection::SSLConnection(std::string hostname, int port, bool nonblock) : Socket::Connection(){ mbedtls_debug_set_threshold(0); isConnected = true; server_fd = new mbedtls_net_context; ssl = new mbedtls_ssl_context; conf = new mbedtls_ssl_config; ctr_drbg = new mbedtls_ctr_drbg_context; entropy = new mbedtls_entropy_context; mbedtls_net_init(server_fd); mbedtls_ssl_init(ssl); mbedtls_ssl_config_init(conf); mbedtls_ctr_drbg_init(ctr_drbg); mbedtls_entropy_init(entropy); DONTEVEN_MSG("SSL init"); if (mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, entropy, (const unsigned char *)"meow", 4) != 0){ FAIL_MSG("SSL socket init failed"); close(); return; } DONTEVEN_MSG("SSL connect"); int ret = 0; if ((ret = mbedtls_net_connect(server_fd, hostname.c_str(), JSON::Value(port).asString().c_str(), MBEDTLS_NET_PROTO_TCP)) != 0){ FAIL_MSG(" failed\n ! mbedtls_net_connect returned %d\n\n", ret); close(); return; } if ((ret = mbedtls_ssl_config_defaults(conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0){ FAIL_MSG(" failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret); close(); return; } mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_NONE); mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, ctr_drbg); mbedtls_ssl_conf_dbg(conf, my_debug, stderr); if ((ret = mbedtls_ssl_setup(ssl, conf)) != 0){ char estr[200]; mbedtls_strerror(ret, estr, 200); FAIL_MSG("SSL setup error %d: %s", ret, estr); close(); return; } if ((ret = mbedtls_ssl_set_hostname(ssl, hostname.c_str())) != 0){ FAIL_MSG(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret); close(); return; } mbedtls_ssl_set_bio(ssl, server_fd, mbedtls_net_send, mbedtls_net_recv, NULL); while ((ret = mbedtls_ssl_handshake(ssl)) != 0){ if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){ char estr[200]; mbedtls_strerror(ret, estr, 200); FAIL_MSG("SSL handshake error %d: %s", ret, estr); close(); return; } } Blocking = true; if (nonblock){setBlocking(false);} } void Socket::SSLConnection::close(){ DONTEVEN_MSG("SSL close"); if (server_fd){ mbedtls_net_free(server_fd); delete server_fd; server_fd = 0; } if (ssl){ mbedtls_ssl_free(ssl); delete ssl; ssl = 0; } if (conf){ mbedtls_ssl_config_free(conf); delete conf; conf = 0; } if (ctr_drbg){ mbedtls_ctr_drbg_free(ctr_drbg); delete ctr_drbg; ctr_drbg = 0; } if (entropy){ mbedtls_entropy_free(entropy); delete entropy; entropy = 0; } isConnected = false; } /// 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::SSLConnection::iread(void *buffer, int len, int flags){ DONTEVEN_MSG("SSL iread"); if (!connected() || len < 1){return 0;} int r; /// \TODO Flags ignored... Bad. r = mbedtls_ssl_read(ssl, (unsigned char *)buffer, len); if (r < 0){ char estr[200]; mbedtls_strerror(r, estr, 200); INFO_MSG("Read returns %d: %s", r, estr); } if (r < 0){ switch (errno){ case MBEDTLS_ERR_SSL_WANT_WRITE: return 0; break; case MBEDTLS_ERR_SSL_WANT_READ: return 0; break; 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; } /// 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::SSLConnection::iwrite(const void *buffer, int len){ DONTEVEN_MSG("SSL iwrite"); if (!connected() || len < 1){return 0;} int r; r = mbedtls_ssl_write(ssl, (const unsigned char *)buffer, len); if (r < 0){ char estr[200]; mbedtls_strerror(r, estr, 200); INFO_MSG("Write returns %d: %s", r, estr); } if (r < 0){ switch (errno){ case MBEDTLS_ERR_SSL_WANT_WRITE: return 0; break; case MBEDTLS_ERR_SSL_WANT_READ: return 0; break; 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 && (sSend >= 0)){ DONTEVEN_MSG("Socket closed by remote"); close(); } up += r; return r; } bool Socket::SSLConnection::connected() const{ return isConnected; } void Socket::SSLConnection::setBlocking(bool blocking){ if (blocking != Blocking){return;} if (blocking){ mbedtls_net_set_block(server_fd); Blocking = true; }else{ mbedtls_net_set_nonblock(server_fd); Blocking = false; } } #endif /// 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)){ FAIL_MSG("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); ERROR_MSG("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 // set interface, 0.0.0.0 (default) is all if (hostname == "0.0.0.0" || hostname.length() == 0){ addr.sin6_addr = in6addr_any; }else{ if (inet_pton(AF_INET6, hostname.c_str(), &addr.sin6_addr) != 1){ errors = strerror(errno); HIGH_MSG("Could not convert '%s' to a valid IPv6 address", hostname.c_str()); close(); return false; } } 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){ DEVEL_MSG("IPv6 socket success @ %s:%i", hostname.c_str(), port); return true; }else{ errors = strerror(errno); ERROR_MSG("IPv6 listen failed! Error: %s", errors.c_str()); close(); return false; } }else{ errors = strerror(errno); ERROR_MSG("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); ERROR_MSG("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 // set interface, 0.0.0.0 (default) is all if (hostname == "0.0.0.0" || hostname.length() == 0){ addr4.sin_addr.s_addr = INADDR_ANY; }else{ if (inet_pton(AF_INET, hostname.c_str(), &addr4.sin_addr) != 1){ errors = strerror(errno); HIGH_MSG("Could not convert '%s' to a valid IPv4 address", hostname.c_str()); close(); return false; } } 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){ DEVEL_MSG("IPv4 socket success @ %s:%i", hostname.c_str(), port); return true; }else{ errors = strerror(errno); ERROR_MSG("IPv4 listen failed! Error: %s", errors.c_str()); close(); return false; } }else{ errors = strerror(errno); ERROR_MSG("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); ERROR_MSG("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); ERROR_MSG("Unix listen failed! Error: %s", errors.c_str()); close(); return; } }else{ errors = strerror(errno); ERROR_MSG("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){ if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EINTR)){ FAIL_MSG("Error during accept: %s. Closing server socket %d.", strerror(errno), sock); close(); } return Socket::Connection(); } if (nonblock){ int flags = fcntl(r, F_GETFL, 0); flags |= O_NONBLOCK; fcntl(r, F_SETFL, flags); } int optval = 1; int optlen = sizeof(optval); setsockopt(r, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); Socket::Connection tmp(r); tmp.remoteaddr = tmpaddr; if (tmpaddr.sin6_family == AF_INET6){ tmp.remotehost = inet_ntop(AF_INET6, &(tmpaddr.sin6_addr), addrconv, INET6_ADDRSTRLEN); HIGH_MSG("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); HIGH_MSG("IPv4 addr [%s]", tmp.remotehost.c_str()); } if (tmpaddr.sin6_family == AF_UNIX){ HIGH_MSG("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){ HIGH_MSG("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){FAIL_MSG("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){FAIL_MSG("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){ FAIL_MSG("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); if (family != rp->ai_family){ INFO_MSG("Socket is wrong type (%s), re-opening as %s", addrFam(family), addrFam(rp->ai_family)); 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; FAIL_MSG("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; FAIL_MSG("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{ FAIL_MSG("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 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; uint16_t portNo = 0; 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); 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); // 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){ // 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){ portNo = ntohs(((struct sockaddr_in6 *)&fin_addr)->sin6_port); }else{ portNo = ntohs(((struct sockaddr_in *)&fin_addr)->sin_port); } } INFO_MSG("UDP bind success on %s:%u (%s)", human_addr, portNo, 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(); } }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(); } } }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(); } freeaddrinfo(reslocal); // free resolved interface addr }// loop over all interfaces } freeaddrinfo(resmulti); // free resolved multicast addr } return portNo; } /// 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; } data_len = 0; return false; } int Socket::UDPConnection::getSock(){ return sock; }