Added hostBytesToStr function to socket library, fixed code style, fixed UDP Cygwin support

This commit is contained in:
Thulinma 2016-11-03 15:15:42 +01:00
parent 567759ce26
commit 629b24853a
3 changed files with 544 additions and 617 deletions

View file

@ -3,15 +3,15 @@
/// Written by Jaron Vietor in 2010 for DDVTech /// Written by Jaron Vietor in 2010 for DDVTech
#include "socket.h" #include "socket.h"
#include "timing.h"
#include "defines.h" #include "defines.h"
#include <sys/stat.h> #include "timing.h"
#include <sys/socket.h> #include <cstdlib>
#include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <poll.h> #include <poll.h>
#include <netdb.h>
#include <sstream> #include <sstream>
#include <cstdlib> #include <sys/socket.h>
#include <sys/stat.h>
#define BUFFER_BLOCKSIZE 4096 // set buffer blocksize to 4KiB #define BUFFER_BLOCKSIZE 4096 // set buffer blocksize to 4KiB
@ -21,6 +21,32 @@
#define SOCKETSIZE 51200ul #define SOCKETSIZE 51200ul
#endif #endif
/// 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::string uint2string(unsigned int i){
std::stringstream st; std::stringstream st;
st << i; st << i;
@ -31,9 +57,7 @@ std::string uint2string(unsigned int i) {
/// The back is popped as long as it is empty, first - this way this function is /// 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. /// guaranteed to return 0 if the buffer is empty.
unsigned int Socket::Buffer::size(){ unsigned int Socket::Buffer::size(){
while (data.size() > 0 && data.back().empty()) { while (data.size() > 0 && data.back().empty()){data.pop_back();}
data.pop_back();
}
return data.size(); return data.size();
} }
@ -42,9 +66,7 @@ unsigned int Socket::Buffer::bytes(unsigned int max) {
unsigned int i = 0; unsigned int i = 0;
for (std::deque<std::string>::iterator it = data.begin(); it != data.end(); ++it){ for (std::deque<std::string>::iterator it = data.begin(); it != data.end(); ++it){
i += (*it).size(); i += (*it).size();
if (i >= max) { if (i >= max){return max;}
return max;
}
} }
return i; return i;
} }
@ -63,12 +85,9 @@ void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize
j = i; j = i;
while (j < newdatasize && j - i <= BUFFER_BLOCKSIZE){ while (j < newdatasize && j - i <= BUFFER_BLOCKSIZE){
j++; j++;
if (newdata[j - 1] == '\n') { if (newdata[j - 1] == '\n'){break;}
break;
}
} }
if (i != j){ if (i != j){
DONTEVEN_MSG("Adding a block of size %d", j - i);
data.push_front(std::string(newdata + i, (size_t)(j - i))); data.push_front(std::string(newdata + i, (size_t)(j - i)));
i = j; i = j;
}else{ }else{
@ -76,7 +95,8 @@ void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize
} }
} }
if (data.size() > 5000){ 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)); DEBUG_MSG(DLVL_WARN, "Warning: After %d new bytes, buffer has %d parts containing over %u bytes!", newdatasize, (int)data.size(),
bytes(9000));
} }
} }
@ -98,9 +118,7 @@ bool Socket::Buffer::available(unsigned int count) {
unsigned int i = 0; unsigned int i = 0;
for (std::deque<std::string>::iterator it = data.begin(); it != data.end(); ++it){ for (std::deque<std::string>::iterator it = data.begin(); it != data.end(); ++it){
i += (*it).size(); i += (*it).size();
if (i >= count) { if (i >= count){return true;}
return true;
}
} }
return false; return false;
} }
@ -109,9 +127,7 @@ bool Socket::Buffer::available(unsigned int count) {
/// Returns an empty string if not all count bytes are available. /// Returns an empty string if not all count bytes are available.
std::string Socket::Buffer::remove(unsigned int count){ std::string Socket::Buffer::remove(unsigned int count){
size(); size();
if (!available(count)) { if (!available(count)){return "";}
return "";
}
unsigned int i = 0; unsigned int i = 0;
std::string ret; std::string ret;
ret.reserve(count); ret.reserve(count);
@ -133,9 +149,7 @@ std::string Socket::Buffer::remove(unsigned int count) {
/// Returns an empty string if not all count bytes are available. /// Returns an empty string if not all count bytes are available.
std::string Socket::Buffer::copy(unsigned int count){ std::string Socket::Buffer::copy(unsigned int count){
size(); size();
if (!available(count)) { if (!available(count)){return "";}
return "";
}
unsigned int i = 0; unsigned int i = 0;
std::string ret; std::string ret;
ret.reserve(count); ret.reserve(count);
@ -207,6 +221,11 @@ Socket::Connection::Connection() {
Blocking = false; Blocking = false;
}// Socket::Connection basic constructor }// Socket::Connection basic constructor
void Socket::Connection::resetCounter(){
up = 0;
down = 0;
}
/// Internally used call to make an file descriptor blocking or not. /// Internally used call to make an file descriptor blocking or not.
void setFDBlocking(int FD, bool blocking){ void setFDBlocking(int FD, bool blocking){
int flags = fcntl(FD, F_GETFL, 0); int flags = fcntl(FD, F_GETFL, 0);
@ -226,28 +245,16 @@ bool isFDBlocking(int FD) {
/// Set this socket to be blocking (true) or nonblocking (false). /// Set this socket to be blocking (true) or nonblocking (false).
void Socket::Connection::setBlocking(bool blocking){ void Socket::Connection::setBlocking(bool blocking){
if (sock >= 0) { if (sock >= 0){setFDBlocking(sock, blocking);}
setFDBlocking(sock, blocking); if (pipes[0] >= 0){setFDBlocking(pipes[0], blocking);}
} if (pipes[1] >= 0){setFDBlocking(pipes[1], 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). /// Set this socket to be blocking (true) or nonblocking (false).
bool Socket::Connection::isBlocking(){ bool Socket::Connection::isBlocking(){
if (sock >= 0) { if (sock >= 0){return isFDBlocking(sock);}
return isFDBlocking(sock); if (pipes[0] >= 0){return isFDBlocking(pipes[0]);}
} if (pipes[1] >= 0){return isFDBlocking(pipes[1]);}
if (pipes[0] >= 0) {
return isFDBlocking(pipes[0]);
}
if (pipes[1] >= 0) {
return isFDBlocking(pipes[1]);
}
return false; return false;
} }
@ -256,9 +263,7 @@ bool Socket::Connection::isBlocking() {
/// This function calls shutdown, thus making the socket unusable in all other /// 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. /// processes as well. Do not use on shared sockets that are still in use.
void Socket::Connection::close(){ void Socket::Connection::close(){
if (sock != -1) { if (sock != -1){shutdown(sock, SHUT_RDWR);}
shutdown(sock, SHUT_RDWR);
}
drop(); drop();
}// Socket::Connection::close }// Socket::Connection::close
@ -271,20 +276,17 @@ void Socket::Connection::drop() {
if (sock != -1){ if (sock != -1){
DEBUG_MSG(DLVL_HIGH, "Socket %d closed", sock); DEBUG_MSG(DLVL_HIGH, "Socket %d closed", sock);
errno = EINTR; errno = EINTR;
while (::close(sock) != 0 && errno == EINTR) { while (::close(sock) != 0 && errno == EINTR){}
}
sock = -1; sock = -1;
} }
if (pipes[0] != -1){ if (pipes[0] != -1){
errno = EINTR; errno = EINTR;
while (::close(pipes[0]) != 0 && errno == EINTR) { while (::close(pipes[0]) != 0 && errno == EINTR){}
}
pipes[0] = -1; pipes[0] = -1;
} }
if (pipes[1] != -1){ if (pipes[1] != -1){
errno = EINTR; errno = EINTR;
while (::close(pipes[1]) != 0 && errno == EINTR) { while (::close(pipes[1]) != 0 && errno == EINTR){}
}
pipes[1] = -1; pipes[1] = -1;
} }
} }
@ -292,15 +294,9 @@ void Socket::Connection::drop() {
/// Returns internal socket number. /// Returns internal socket number.
int Socket::Connection::getSocket(){ int Socket::Connection::getSocket(){
if (sock != -1) { if (sock != -1){return sock;}
return sock; if (pipes[0] != -1){return pipes[0];}
} if (pipes[1] != -1){return pipes[1];}
if (pipes[0] != -1) {
return pipes[0];
}
if (pipes[1] != -1) {
return pipes[1];
}
return -1; return -1;
} }
@ -379,12 +375,8 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock) {
remotehost = ""; remotehost = "";
for (rp = result; rp != NULL; rp = rp->ai_next){ for (rp = result; rp != NULL; rp = rp->ai_next){
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sock < 0) { if (sock < 0){continue;}
continue; if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){break;}
}
if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0) {
break;
}
remotehost += strerror(errno); remotehost += strerror(errno);
::close(sock); ::close(sock);
} }
@ -458,16 +450,10 @@ Socket::Buffer & Socket::Connection::Received() {
/// Any data that could not be send will block until it can be send or the connection is severed. /// 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){ void Socket::Connection::SendNow(const char *data, size_t len){
bool bing = isBlocking(); bool bing = isBlocking();
if (!bing) { if (!bing){setBlocking(true);}
setBlocking(true);
}
unsigned int i = iwrite(data, std::min((long unsigned int)len, SOCKETSIZE)); unsigned int i = iwrite(data, std::min((long unsigned int)len, SOCKETSIZE));
while (i < len && connected()) { while (i < len && connected()){i += iwrite(data + i, std::min((long unsigned int)(len - i), SOCKETSIZE));}
i += iwrite(data + i, std::min((long unsigned int)(len - i), SOCKETSIZE)); if (!bing){setBlocking(false);}
}
if (!bing) {
setBlocking(false);
}
} }
/// Will not buffer anything but always send right away. Blocks. /// Will not buffer anything but always send right away. Blocks.
@ -489,9 +475,7 @@ void Socket::Connection::SendNow(const std::string & data) {
/// \param len Amount of bytes to write. /// \param len Amount of bytes to write.
/// \returns The amount of bytes actually written. /// \returns The amount of bytes actually written.
unsigned int Socket::Connection::iwrite(const void *buffer, int len){ unsigned int Socket::Connection::iwrite(const void *buffer, int len){
if (!connected() || len < 1) { if (!connected() || len < 1){return 0;}
return 0;
}
int r; int r;
if (sock >= 0){ if (sock >= 0){
r = send(sock, buffer, len, 0); r = send(sock, buffer, len, 0);
@ -500,9 +484,7 @@ unsigned int Socket::Connection::iwrite(const void * buffer, int len) {
} }
if (r < 0){ if (r < 0){
switch (errno){ switch (errno){
case EWOULDBLOCK: case EWOULDBLOCK: return 0; break;
return 0;
break;
default: default:
Error = true; Error = true;
INSANE_MSG("Could not iwrite data! Error: %s", strerror(errno)); INSANE_MSG("Could not iwrite data! Error: %s", strerror(errno));
@ -526,26 +508,18 @@ unsigned int Socket::Connection::iwrite(const void * buffer, int len) {
/// \param flags Flags to use in the recv call. Ignored on fake sockets. /// \param flags Flags to use in the recv call. Ignored on fake sockets.
/// \returns The amount of bytes actually read. /// \returns The amount of bytes actually read.
int Socket::Connection::iread(void *buffer, int len, int flags){ int Socket::Connection::iread(void *buffer, int len, int flags){
if (!connected() || len < 1) { if (!connected() || len < 1){return 0;}
return 0;
}
int r; int r;
if (sock >= 0){ if (sock >= 0){
r = recv(sock, buffer, len, flags); r = recv(sock, buffer, len, flags);
}else{ }else{
r = recv(pipes[1], buffer, len, flags); r = recv(pipes[1], buffer, len, flags);
if (r < 0 && errno == ENOTSOCK) { if (r < 0 && errno == ENOTSOCK){r = read(pipes[1], buffer, len);}
r = read(pipes[1], buffer, len);
}
} }
if (r < 0){ if (r < 0){
switch (errno){ switch (errno){
case EWOULDBLOCK: case EWOULDBLOCK: return 0; break;
return 0; case EINTR: return 0; break;
break;
case EINTR:
return 0;
break;
default: default:
Error = true; Error = true;
INSANE_MSG("Could not iread data! Error: %s", strerror(errno)); INSANE_MSG("Could not iread data! Error: %s", strerror(errno));
@ -571,9 +545,7 @@ int Socket::Connection::iread(void * buffer, int len, int flags) {
bool Socket::Connection::iread(Buffer &buffer, int flags){ bool Socket::Connection::iread(Buffer &buffer, int flags){
char cbuffer[BUFFER_BLOCKSIZE]; char cbuffer[BUFFER_BLOCKSIZE];
int num = iread(cbuffer, BUFFER_BLOCKSIZE, flags); int num = iread(cbuffer, BUFFER_BLOCKSIZE, flags);
if (num < 1) { if (num < 1){return false;}
return false;
}
buffer.append(cbuffer, num); buffer.append(cbuffer, num);
return true; return true;
}// iread }// iread
@ -584,13 +556,9 @@ bool Socket::Connection::iread(Buffer & buffer, int flags) {
/// \param buffer std::string to remove data from. /// \param buffer std::string to remove data from.
/// \return True if more data was sent, false otherwise. /// \return True if more data was sent, false otherwise.
bool Socket::Connection::iwrite(std::string &buffer){ bool Socket::Connection::iwrite(std::string &buffer){
if (buffer.size() < 1) { if (buffer.size() < 1){return false;}
return false;
}
unsigned int tmp = iwrite((void *)buffer.c_str(), buffer.size()); unsigned int tmp = iwrite((void *)buffer.c_str(), buffer.size());
if (!tmp) { if (!tmp){return false;}
return false;
}
buffer = buffer.substr(tmp); buffer = buffer.substr(tmp);
return true; return true;
}// iwrite }// iwrite
@ -620,12 +588,8 @@ std::string Socket::Connection::getBinHost() {
} }
char tmpBuffer[17] = "\000\000\000\000\000\000\000\000\000\000\377\377\000\000\000\000"; char tmpBuffer[17] = "\000\000\000\000\000\000\000\000\000\000\377\377\000\000\000\000";
for (rp = result; rp != NULL; rp = rp->ai_next){ for (rp = result; rp != NULL; rp = rp->ai_next){
if (rp->ai_family == AF_INET) { if (rp->ai_family == AF_INET){memcpy(tmpBuffer + 12, &((sockaddr_in *)rp->ai_addr)->sin_addr.s_addr, 4);}
memcpy(tmpBuffer + 12, &((sockaddr_in *)rp->ai_addr)->sin_addr.s_addr, 4); if (rp->ai_family == AF_INET6){memcpy(tmpBuffer, ((sockaddr_in6 *)rp->ai_addr)->sin6_addr.s6_addr, 16);}
}
if (rp->ai_family == AF_INET6) {
memcpy(tmpBuffer, ((sockaddr_in6 *)rp->ai_addr)->sin6_addr.s6_addr, 16);
}
} }
freeaddrinfo(result); freeaddrinfo(result);
return std::string(tmpBuffer, 16); return std::string(tmpBuffer, 16);
@ -671,28 +635,20 @@ bool Socket::Connection::isAddress(std::string addr) {
hints.ai_addr = NULL; hints.ai_addr = NULL;
hints.ai_next = NULL; hints.ai_next = NULL;
int s = getaddrinfo(addr.c_str(), 0, &hints, &result); int s = getaddrinfo(addr.c_str(), 0, &hints, &result);
if (s != 0) { if (s != 0){return false;}
return false;
}
char newaddr[INET_ADDRSTRLEN]; char newaddr[INET_ADDRSTRLEN];
newaddr[0] = 0; newaddr[0] = 0;
for (rp = result; rp != NULL; rp = rp->ai_next){ for (rp = result; rp != NULL; rp = rp->ai_next){
if (rp->ai_family == AF_INET && inet_ntop(rp->ai_family, &(((sockaddr_in *)rp->ai_addr)->sin_addr), newaddr, INET_ADDRSTRLEN)){ if (rp->ai_family == AF_INET && inet_ntop(rp->ai_family, &(((sockaddr_in *)rp->ai_addr)->sin_addr), newaddr, INET_ADDRSTRLEN)){
DEBUG_MSG(DLVL_DEVEL, "Comparing: '%s' to '%s'", remotehost.c_str(), newaddr); DEBUG_MSG(DLVL_DEVEL, "Comparing: '%s' to '%s'", remotehost.c_str(), newaddr);
if (remotehost == newaddr) { if (remotehost == newaddr){return true;}
return true;
}
DEBUG_MSG(DLVL_DEVEL, "Comparing: '%s' to '::ffff:%s'", remotehost.c_str(), newaddr); DEBUG_MSG(DLVL_DEVEL, "Comparing: '%s' to '::ffff:%s'", remotehost.c_str(), newaddr);
if (remotehost == std::string("::ffff:") + newaddr) { if (remotehost == std::string("::ffff:") + newaddr){return true;}
return true;
}
} }
if (rp->ai_family == AF_INET6 && inet_ntop(rp->ai_family, &(((sockaddr_in6 *)rp->ai_addr)->sin6_addr), newaddr, INET_ADDRSTRLEN)){ if (rp->ai_family == AF_INET6 && inet_ntop(rp->ai_family, &(((sockaddr_in6 *)rp->ai_addr)->sin6_addr), newaddr, INET_ADDRSTRLEN)){
DEBUG_MSG(DLVL_DEVEL, "Comparing: '%s' to '%s'", remotehost.c_str(), newaddr); DEBUG_MSG(DLVL_DEVEL, "Comparing: '%s' to '%s'", remotehost.c_str(), newaddr);
if (remotehost == newaddr) { if (remotehost == newaddr){return true;}
return true;
}
} }
} }
freeaddrinfo(result); freeaddrinfo(result);
@ -820,7 +776,8 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock) {
/// Create a new Unix Server. The socket is immediately bound and set to listen. /// 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. /// A maximum of 100 connections will be accepted between accept() calls.
/// Any further connections coming in will be dropped. /// 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! /// 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 address The location of the Unix socket to bind to.
/// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). /// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking).
Socket::Server::Server(std::string address, bool nonblock){ Socket::Server::Server(std::string address, bool nonblock){
@ -863,9 +820,7 @@ Socket::Server::Server(std::string address, bool nonblock) {
/// \param nonblock (optional) Whether the newly connected socket should be nonblocking. Default is false (blocking). /// \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. /// \returns A Socket::Connection, which may or may not be connected, depending on settings and circumstances.
Socket::Connection Socket::Server::accept(bool nonblock){ Socket::Connection Socket::Server::accept(bool nonblock){
if (sock < 0) { if (sock < 0){return Socket::Connection(-1);}
return Socket::Connection(-1);
}
struct sockaddr_in6 addrinfo; struct sockaddr_in6 addrinfo;
socklen_t len = sizeof(addrinfo); socklen_t len = sizeof(addrinfo);
static char addrconv[INET6_ADDRSTRLEN]; static char addrconv[INET6_ADDRSTRLEN];
@ -902,16 +857,12 @@ Socket::Connection Socket::Server::accept(bool nonblock) {
/// Set this socket to be blocking (true) or nonblocking (false). /// Set this socket to be blocking (true) or nonblocking (false).
void Socket::Server::setBlocking(bool blocking){ void Socket::Server::setBlocking(bool blocking){
if (sock >= 0) { if (sock >= 0){setFDBlocking(sock, blocking);}
setFDBlocking(sock, blocking);
}
} }
/// Set this socket to be blocking (true) or nonblocking (false). /// Set this socket to be blocking (true) or nonblocking (false).
bool Socket::Server::isBlocking(){ bool Socket::Server::isBlocking(){
if (sock >= 0) { if (sock >= 0){return isFDBlocking(sock);}
return isFDBlocking(sock);
}
return false; return false;
} }
@ -920,9 +871,7 @@ bool Socket::Server::isBlocking() {
/// This function calls shutdown, thus making the socket unusable in all other /// 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. /// processes as well. Do not use on shared sockets that are still in use.
void Socket::Server::close(){ void Socket::Server::close(){
if (sock != -1) { if (sock != -1){shutdown(sock, SHUT_RDWR);}
shutdown(sock, SHUT_RDWR);
}
drop(); drop();
}// Socket::Server::close }// Socket::Server::close
@ -935,8 +884,7 @@ void Socket::Server::drop() {
if (sock != -1){ if (sock != -1){
DEBUG_MSG(DLVL_HIGH, "ServerSocket %d closed", sock); DEBUG_MSG(DLVL_HIGH, "ServerSocket %d closed", sock);
errno = EINTR; errno = EINTR;
while (::close(sock) != 0 && errno == EINTR) { while (::close(sock) != 0 && errno == EINTR){}
}
sock = -1; sock = -1;
} }
} }
@ -967,9 +915,7 @@ Socket::UDPConnection::UDPConnection(bool nonblock) {
sock = socket(AF_INET, SOCK_DGRAM, 0); sock = socket(AF_INET, SOCK_DGRAM, 0);
family = AF_INET; family = AF_INET;
} }
if (sock == -1) { if (sock == -1){DEBUG_MSG(DLVL_FAIL, "Could not create UDP socket: %s", strerror(errno));}
DEBUG_MSG(DLVL_FAIL, "Could not create UDP socket: %s", strerror(errno));
}
up = 0; up = 0;
down = 0; down = 0;
destAddr = 0; destAddr = 0;
@ -977,9 +923,7 @@ Socket::UDPConnection::UDPConnection(bool nonblock) {
data = 0; data = 0;
data_size = 0; data_size = 0;
data_len = 0; data_len = 0;
if (nonblock) { if (nonblock){setBlocking(!nonblock);}
setBlocking(!nonblock);
}
}// Socket::UDPConnection UDP Contructor }// Socket::UDPConnection UDP Contructor
/// Copies a UDP socket, re-allocating local copies of any needed structures. /// Copies a UDP socket, re-allocating local copies of any needed structures.
@ -991,16 +935,12 @@ Socket::UDPConnection::UDPConnection(const UDPConnection & o) {
sock = socket(AF_INET, SOCK_DGRAM, 0); sock = socket(AF_INET, SOCK_DGRAM, 0);
family = AF_INET; family = AF_INET;
} }
if (sock == -1) { if (sock == -1){DEBUG_MSG(DLVL_FAIL, "Could not create UDP socket: %s", strerror(errno));}
DEBUG_MSG(DLVL_FAIL, "Could not create UDP socket: %s", strerror(errno));
}
up = 0; up = 0;
down = 0; down = 0;
if (o.destAddr && o.destAddr_size){ if (o.destAddr && o.destAddr_size){
destAddr = malloc(o.destAddr_size); destAddr = malloc(o.destAddr_size);
if (destAddr) { if (destAddr){memcpy(destAddr, o.destAddr, o.destAddr_size);}
memcpy(destAddr, o.destAddr, o.destAddr_size);
}
}else{ }else{
destAddr = 0; destAddr = 0;
destAddr_size = 0; destAddr_size = 0;
@ -1018,8 +958,7 @@ Socket::UDPConnection::UDPConnection(const UDPConnection & o) {
void Socket::UDPConnection::close(){ void Socket::UDPConnection::close(){
if (sock != -1){ if (sock != -1){
errno = EINTR; errno = EINTR;
while (::close(sock) != 0 && errno == EINTR) { while (::close(sock) != 0 && errno == EINTR){}
}
sock = -1; sock = -1;
} }
} }
@ -1040,26 +979,20 @@ Socket::UDPConnection::~UDPConnection() {
/// Stores the properties of the receiving end of this UDP socket. /// Stores the properties of the receiving end of this UDP socket.
/// This will be the receiving end for all SendNow calls. /// This will be the receiving end for all SendNow calls.
void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port){ void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port){
if (destAddr) { //UDP sockets can switch between IPv4 and IPv6 on demand.
free(destAddr); //We change IPv4-mapped IPv6 addresses into IPv4 addresses for Windows-sillyness reasons.
destAddr = 0; if (destIp.substr(0, 7) == "::ffff:"){
destIp = destIp.substr(7);
} }
destAddr = malloc(sizeof(struct sockaddr_in6));
if (!destAddr) {
return;
}
destAddr_size = sizeof(struct sockaddr_in6);
memset(destAddr, 0, destAddr_size);
struct addrinfo *result, *rp, hints; struct addrinfo *result, *rp, hints;
std::stringstream ss; std::stringstream ss;
ss << port; ss << port;
memset(&hints, 0, sizeof(struct addrinfo)); memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = family; hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM; hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_ADDRCONFIG; hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED;
hints.ai_protocol = 0; hints.ai_protocol = IPPROTO_UDP;
hints.ai_canonname = NULL; hints.ai_canonname = NULL;
hints.ai_addr = NULL; hints.ai_addr = NULL;
hints.ai_next = NULL; hints.ai_next = NULL;
@ -1071,7 +1004,17 @@ void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port) {
for (rp = result; rp != NULL; rp = rp->ai_next){ for (rp = result; rp != NULL; rp = rp->ai_next){
// assume success // 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); memcpy(destAddr, rp->ai_addr, rp->ai_addrlen);
close();
family = rp->ai_family;
sock = socket(family, SOCK_DGRAM, 0);
freeaddrinfo(result); freeaddrinfo(result);
return; return;
//\todo Possibly detect and handle failure //\todo Possibly detect and handle failure
@ -1115,21 +1058,15 @@ void Socket::UDPConnection::GetDestination(std::string & destIp, uint32_t & port
/// Returns 0 on error. /// Returns 0 on error.
uint32_t Socket::UDPConnection::getDestPort() const{ uint32_t Socket::UDPConnection::getDestPort() const{
if (!destAddr || !destAddr_size){return 0;} if (!destAddr || !destAddr_size){return 0;}
if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET6) { if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET6){return ntohs(((struct sockaddr_in6 *)destAddr)->sin6_port);}
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);}
}
if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET) {
return ntohs(((struct sockaddr_in *)destAddr)->sin_port);
}
return 0; return 0;
} }
/// Sets the socket to be blocking if the parameters is true. /// Sets the socket to be blocking if the parameters is true.
/// Sets the socket to be non-blocking otherwise. /// Sets the socket to be non-blocking otherwise.
void Socket::UDPConnection::setBlocking(bool blocking){ void Socket::UDPConnection::setBlocking(bool blocking){
if (sock >= 0) { if (sock >= 0){setFDBlocking(sock, blocking);}
setFDBlocking(sock, blocking);
}
} }
/// Sends a UDP datagram using the buffer sdata. /// Sends a UDP datagram using the buffer sdata.
@ -1150,9 +1087,7 @@ void Socket::UDPConnection::SendNow(const char * sdata) {
/// Does not do anything if len < 1. /// Does not do anything if len < 1.
/// Prints an DLVL_FAIL level debug message if sending failed. /// Prints an DLVL_FAIL level debug message if sending failed.
void Socket::UDPConnection::SendNow(const char *sdata, size_t len){ void Socket::UDPConnection::SendNow(const char *sdata, size_t len){
if (len < 1) { if (len < 1){return;}
return;
}
int r = sendto(sock, sdata, len, 0, (sockaddr *)destAddr, destAddr_size); int r = sendto(sock, sdata, len, 0, (sockaddr *)destAddr, destAddr_size);
if (r > 0){ if (r > 0){
up += r; up += r;
@ -1165,7 +1100,8 @@ void Socket::UDPConnection::SendNow(const char * sdata, size_t len) {
/// If that fails, returns zero. /// If that fails, returns zero.
/// \arg port Port to bind to, required. /// \arg port Port to bind to, required.
/// \arg iface Interface address to listen for packets on (may be multicast address) /// \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. /// \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. /// \return Actually bound port number, or zero on error.
int Socket::UDPConnection::bind(int port, std::string iface, const std::string &multicastInterfaces){ int Socket::UDPConnection::bind(int port, std::string iface, const std::string &multicastInterfaces){
close(); // we open a new socket for each attempt close(); // we open a new socket for each attempt
@ -1175,7 +1111,7 @@ int Socket::UDPConnection::bind(int port, std::string iface, const std::string &
struct addrinfo hints, *addr_result, *rp; struct addrinfo hints, *addr_result, *rp;
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_PASSIVE; hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_PASSIVE;
hints.ai_family = AF_UNSPEC; hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM; hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP; hints.ai_protocol = IPPROTO_UDP;
@ -1197,9 +1133,7 @@ int Socket::UDPConnection::bind(int port, std::string iface, const std::string &
std::string err_str; std::string err_str;
for (rp = addr_result; rp != NULL; rp = rp->ai_next){ for (rp = addr_result; rp != NULL; rp = rp->ai_next){
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sock == -1) { if (sock == -1){continue;}
continue;
}
char human_addr[INET6_ADDRSTRLEN]; char human_addr[INET6_ADDRSTRLEN];
getnameinfo(rp->ai_addr, rp->ai_addrlen, human_addr, INET6_ADDRSTRLEN, 0, 0, NI_NUMERICHOST); getnameinfo(rp->ai_addr, rp->ai_addrlen, human_addr, INET6_ADDRSTRLEN, 0, 0, NI_NUMERICHOST);
MEDIUM_MSG("Attempting bind to %s (%s)", human_addr, rp->ai_family == AF_INET6 ? "IPv6" : "IPv4"); MEDIUM_MSG("Attempting bind to %s (%s)", human_addr, rp->ai_family == AF_INET6 ? "IPv6" : "IPv4");
@ -1292,7 +1226,8 @@ int Socket::UDPConnection::bind(int port, std::string iface, const std::string &
memcpy(&mreq6.ipv6mr_multiaddr, &((sockaddr_in6 *)resmulti->ai_addr)->sin6_addr, sizeof(mreq6.ipv6mr_multiaddr)); 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; mreq6.ipv6mr_interface = ((sockaddr_in6 *)reslocal->ai_addr)->sin6_scope_id;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq6, sizeof(mreq6)) != 0){ 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)); 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{ }else{
atLeastOne = true; atLeastOne = true;
} }
@ -1318,7 +1253,6 @@ int Socket::UDPConnection::bind(int port, std::string iface, const std::string &
}// loop over all interfaces }// loop over all interfaces
} }
freeaddrinfo(resmulti); // free resolved multicast addr freeaddrinfo(resmulti); // free resolved multicast addr
} }
return result; return result;
} }
@ -1336,9 +1270,7 @@ bool Socket::UDPConnection::Receive() {
#endif #endif
int r = recvfrom(sock, data, data_size, MSG_PEEK | MSG_TRUNC | MSG_DONTWAIT, 0, 0); int r = recvfrom(sock, data, data_size, MSG_PEEK | MSG_TRUNC | MSG_DONTWAIT, 0, 0);
if (r == -1){ if (r == -1){
if (errno != EAGAIN) { if (errno != EAGAIN){INFO_MSG("UDP receive: %d (%s)", errno, strerror(errno));}
INFO_MSG("UDP receive: %d (%s)", errno, strerror(errno));
}
data_len = 0; data_len = 0;
return false; return false;
} }

View file

@ -3,18 +3,18 @@
/// Written by Jaron Vietor in 2010 for DDVTech /// Written by Jaron Vietor in 2010 for DDVTech
#pragma once #pragma once
#include <string>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <deque> #include <deque>
#include <errno.h>
#include <fcntl.h>
#include <sstream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
// for being friendly with Socket::Connection down below // for being friendly with Socket::Connection down below
namespace Buffer{ namespace Buffer{
@ -24,10 +24,13 @@ namespace Buffer {
/// Holds Socket tools. /// Holds Socket tools.
namespace Socket{ namespace Socket{
void hostBytesToStr(const char *bytes, size_t len, std::string &target);
/// A buffer made out of std::string objects that can be efficiently read from and written to. /// A buffer made out of std::string objects that can be efficiently read from and written to.
class Buffer{ class Buffer{
private: private:
std::deque<std::string> data; std::deque<std::string> data;
public: public:
unsigned int size(); unsigned int size();
unsigned int bytes(unsigned int max); unsigned int bytes(unsigned int max);
@ -90,6 +93,7 @@ namespace Socket {
unsigned int connTime(); ///< Returns the time this socket has been connected. unsigned int connTime(); ///< Returns the time this socket has been connected.
uint64_t dataUp(); ///< Returns total amount of bytes sent. uint64_t dataUp(); ///< Returns total amount of bytes sent.
uint64_t dataDown(); ///< Returns total amount of bytes received. uint64_t dataDown(); ///< Returns total amount of bytes received.
void resetCounter(); ///< Resets the up/down bytes counter to zero.
std::string getStats(std::string C); ///< Returns a std::string of stats, ended by a newline. std::string getStats(std::string C); ///< Returns a std::string of stats, ended by a newline.
friend class Server; friend class Server;
bool Error; ///< Set to true if a socket error happened. bool Error; ///< Set to true if a socket error happened.
@ -148,5 +152,5 @@ namespace Socket {
void SendNow(const char *data); void SendNow(const char *data);
void SendNow(const char *data, size_t len); void SendNow(const char *data, size_t len);
}; };
} }

View file

@ -49,16 +49,7 @@ std::string Controller::sessIndex::toStr(){
/// Initializes a sessIndex from a statExchange object, converting binary format IP addresses into strings. /// Initializes a sessIndex from a statExchange object, converting binary format IP addresses into strings.
/// This extracts the host, stream name, connector and crc field, ignoring everything else. /// This extracts the host, stream name, connector and crc field, ignoring everything else.
Controller::sessIndex::sessIndex(IPC::statExchange & data){ Controller::sessIndex::sessIndex(IPC::statExchange & data){
std::string tHost = data.host(); Socket::hostBytesToStr(data.host().c_str(), 16, host);
if (tHost.substr(0, 12) == std::string("\000\000\000\000\000\000\000\000\000\000\377\377", 12)){
char tmpstr[16];
snprintf(tmpstr, 16, "%hhu.%hhu.%hhu.%hhu", tHost[12], tHost[13], tHost[14], tHost[15]);
host = 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", tHost[0], tHost[1], tHost[2], tHost[3], tHost[4], tHost[5], tHost[6], tHost[7], tHost[8], tHost[9], tHost[10], tHost[11], tHost[12], tHost[13], tHost[14], tHost[15]);
host = tmpstr;
}
streamName = data.streamName(); streamName = data.streamName();
connector = data.connector(); connector = data.connector();
crc = data.crc(); crc = data.crc();