From bb9bc9051629c3319c56785c2f4ae0e605d76329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sat, 21 Nov 2015 14:51:17 +0100 Subject: Initial import of bitz-server version 0.1.6-1 --- lib/socket/socket.cpp | 472 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 472 insertions(+) create mode 100644 lib/socket/socket.cpp (limited to 'lib/socket/socket.cpp') diff --git a/lib/socket/socket.cpp b/lib/socket/socket.cpp new file mode 100644 index 0000000..a086d95 --- /dev/null +++ b/lib/socket/socket.cpp @@ -0,0 +1,472 @@ +/* + * C++ sockets on Unix and Windows + * Copyright (C) 2002 + * Copyright (C) 2010 Uditha Atukorala + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#ifndef WIN32 + #include // For data types + #include // For socket(), connect(), send(), and recv() + #include // For gethostbyname() + #include // For inet_addr() + #include // For close() +#endif + +#include // For errno +#include // For strerror +#include // For atoi() + +using namespace std; + +namespace socketlibrary { + +#ifdef WIN32 + static bool initialized = false; +#endif + +// SocketException Code + +SocketException::SocketException(const string &message, bool inclSysMsg) throw() : userMessage(message) { + if (inclSysMsg) { + userMessage.append(": "); + userMessage.append(strerror(errno)); + } +} + +SocketException::~SocketException() throw() { +} + +const char *SocketException::what() const throw() { + return userMessage.c_str(); +} + +// Function to fill in address structure given an address and port +void fillAddr(const string &address, unsigned short port, sockaddr_in &addr) { + + memset(&addr, 0, sizeof(addr)); // Zero out address structure + addr.sin_family = AF_INET; // Internet address + + hostent *host; // Resolve name + if ((host = gethostbyname(address.c_str())) == NULL) { + // strerror() will not work for gethostbyname() and hstrerror() + // is supposedly obsolete + throw SocketException("Failed to resolve name (gethostbyname())"); + } + + addr.sin_addr.s_addr = *((unsigned long *) host->h_addr_list[0]); + addr.sin_port = htons(port); // Assign port in network byte order + +} + +// Socket Code + +Socket::Socket(int type, int protocol) throw(SocketException) { +#ifdef WIN32 + if (!initialized) { + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 0); // Request WinSock v2.0 + if (WSAStartup(wVersionRequested, &wsaData) != 0) { // Load WinSock DLL + throw SocketException("Unable to load WinSock DLL"); + } + initialized = true; + } +#endif + + // Make a new socket + if ((sock = socket(PF_INET, type, protocol)) < 0) { + throw SocketException("Socket creation failed (socket())", true); + } +} + +Socket::Socket(int sock) { + this->sock = sock; +} + +Socket::~Socket() { + +#ifdef WIN32 + ::closesocket(sock); +#else + ::close(sock); +#endif + sock = -1; +} + +string Socket::getLocalAddress() throw(SocketException) { + sockaddr_in addr; + unsigned int addr_len = sizeof(addr); + + if (getsockname(sock, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) { + throw SocketException("Fetch of local address failed (getsockname())", true); + } + return inet_ntoa(addr.sin_addr); +} + +unsigned short Socket::getLocalPort() throw(SocketException) { + sockaddr_in addr; + unsigned int addr_len = sizeof(addr); + + if (getsockname(sock, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) { + throw SocketException("Fetch of local port failed (getsockname())", true); + } + return ntohs(addr.sin_port); +} + +void Socket::setLocalPort(unsigned short localPort) throw(SocketException) { + // Bind the socket to its port + sockaddr_in localAddr; + memset(&localAddr, 0, sizeof(localAddr)); + localAddr.sin_family = AF_INET; + localAddr.sin_addr.s_addr = htonl(INADDR_ANY); + localAddr.sin_port = htons(localPort); + + if (bind(sock, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0) { + throw SocketException("Set of local port failed (bind())", true); + } +} + +void Socket::setLocalAddressAndPort(const string &localAddress, unsigned short localPort) throw(SocketException) { + // Get the address of the requested host + sockaddr_in localAddr; + fillAddr(localAddress, localPort, localAddr); + + if (bind(sock, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0) { + throw SocketException("Set of local address and port failed (bind())", true); + } +} + +void Socket::cleanUp() throw(SocketException) { +#ifdef WIN32 + if (WSACleanup() != 0) { + throw SocketException("WSACleanup() failed"); + } +#endif +} + +unsigned short Socket::resolveService(const string &service, const string &protocol) { + + struct servent *serv; /* Structure containing service information */ + + if ((serv = getservbyname(service.c_str(), protocol.c_str())) == NULL) + return atoi(service.c_str()); /* Service is port number */ + else + return ntohs(serv->s_port); /* Found port (network byte order) by name */ + +} + +// CommunicatingSocket Code + +CommunicatingSocket::CommunicatingSocket(int type, int protocol) throw(SocketException) : Socket(type, protocol) { +} + +CommunicatingSocket::CommunicatingSocket(int newSD) : Socket(newSD) { +} + +void CommunicatingSocket::connect(const string &foreignAddress, unsigned short foreignPort) throw(SocketException) { + // Get the address of the requested host + sockaddr_in destAddr; + fillAddr(foreignAddress, foreignPort, destAddr); + + // Try to connect to the given port + if (::connect(sock, (sockaddr *) &destAddr, sizeof(destAddr)) < 0) { + throw SocketException("Connect failed (connect())", true); + } +} + +void CommunicatingSocket::send(const void *buffer, int bufferLen) throw(SocketException) { + if (::send(sock, (raw_type *) buffer, bufferLen, 0) < 0) { + throw SocketException("Send failed (send())", true); + } +} + +int CommunicatingSocket::recv(void *buffer, int bufferLen) throw(SocketException) { + int rtn; + if ((rtn = ::recv(sock, (raw_type *) buffer, bufferLen, 0)) < 0) { + throw SocketException("Received failed (recv()) for socket ", true); + } + return rtn; +} + +int CommunicatingSocket::peek(void *buffer, int bufferLen) throw(SocketException) { + int rtn; + if ((rtn = ::recv(sock, (raw_type *) buffer, bufferLen, MSG_PEEK)) < 0) { + throw SocketException("Received failed (recv()) for socket ", true); + } + return rtn; +} + +int CommunicatingSocket::readLine(char *buffer, int bufferLen, const char delimiter) throw(SocketException) { + + int n, rc; + char c; + + for (n = 1; n < bufferLen; n++) { + if ((rc = recv(&c, 1)) == 1) { + *buffer++ = c; + if (c == delimiter) { + break; + } + } else if (rc == 0) { + if (n == 1) { + return 0; // EOF, no data read + } else { + break; // EOF, read some data + } + } else { + throw SocketException("Failed to read data from socket ", true); + } + } + + *buffer = '\0'; + return n; + +} + +string CommunicatingSocket::getForeignAddress() throw(SocketException) { + sockaddr_in addr; + unsigned int addr_len = sizeof(addr); + + if (getpeername(sock, (sockaddr *) &addr,(socklen_t *) &addr_len) < 0) { + throw SocketException("Fetch of foreign address failed (getpeername())", true); + } + return inet_ntoa(addr.sin_addr); +} + +unsigned short CommunicatingSocket::getForeignPort() throw(SocketException) { + sockaddr_in addr; + unsigned int addr_len = sizeof(addr); + + if (getpeername(sock, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) { + throw SocketException("Fetch of foreign port failed (getpeername())", true); + } + return ntohs(addr.sin_port); +} + +// TCPSocket Code + +TCPSocket::TCPSocket() throw(SocketException) : CommunicatingSocket(SOCK_STREAM, IPPROTO_TCP) { +} + +TCPSocket::TCPSocket(const string &foreignAddress, unsigned short foreignPort) throw(SocketException) : CommunicatingSocket(SOCK_STREAM, IPPROTO_TCP) { + connect(foreignAddress, foreignPort); +} + +TCPSocket::TCPSocket(int newSD) : CommunicatingSocket(newSD) { +} + +// TCPServerSocket Code + +TCPServerSocket::TCPServerSocket(unsigned short localPort, int queueLen) throw(SocketException) : Socket(SOCK_STREAM, IPPROTO_TCP) { + setLocalPort(localPort); + setListen(queueLen); +} + +TCPServerSocket::TCPServerSocket(const string &localAddress, unsigned short localPort, int queueLen) throw(SocketException) : Socket(SOCK_STREAM, IPPROTO_TCP) { + setLocalAddressAndPort(localAddress, localPort); + setListen(queueLen); +} + +TCPSocket *TCPServerSocket::accept() throw(SocketException) { + int newSD; + if ((newSD = ::accept(sock, NULL, 0)) < 0) { + throw SocketException("Accept failed (accept())", true); + } + return new TCPSocket(newSD); +} + +void TCPServerSocket::setListen(int queueLen) throw(SocketException) { + if (listen(sock, queueLen) < 0) { + throw SocketException("Set listening socket failed (listen())", true); + } +} + +// TCPServerSocketM Code + +TCPServerSocketM::TCPServerSocketM(unsigned short localPort, int queueLen) throw(SocketException) : TCPServerSocket(localPort, queueLen) { + + FD_ZERO(&fds_master); // clear the master and temp sets + + FD_SET(sock, &fds_master); // Add ourself to the master set + fdmax = sock; // keep track of the biggest file descriptor, only ourself for the moment + +} + +TCPSocket *TCPServerSocketM::accept() throw(SocketException) { + + TCPSocket *newClient = TCPServerSocket::accept(); + int newSD = newClient->sock; + + FD_SET(newSD, &fds_master); // add to master set + if (newSD > fdmax) { // keep track of the max + fdmax = newSD; + } + + v_clients.push_back(newClient); + return newClient; + +} + +bool TCPServerSocketM::pendingConnections() throw (SocketException) { + + fd_set read_fds; + FD_ZERO(&read_fds); + + read_fds = fds_master; + if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) { + throw SocketException("Failed to select read fds (select())", true); + } + + if (FD_ISSET(sock, &read_fds)) { + return true; + } else { + return false; + } + +} + +int TCPServerSocketM::getWaitingClients(vector &clients) throw (SocketException) { + + fd_set read_fds; + FD_ZERO(&read_fds); + + int cl_count = 0; + + read_fds = fds_master; + if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) { + throw SocketException("Failed to select read fds (select())", true); + } + + // resize the vector so it doesn't contain any old information + clients.resize(0); + + for(unsigned int i=0; isock, &read_fds)) { // we got one!! + clients.push_back(v_clients.at(i)); + cl_count++; + } + } + + return cl_count; +} + +void TCPServerSocketM::closeClientConnection(TCPSocket * client) { + + for(unsigned int i=0; isock == v_clients.at(i)->sock) { + FD_CLR(client->sock, &fds_master); + v_clients.erase(v_clients.begin() + i); + delete client; + break; + } + } + +} + + +// UDPSocket Code + +UDPSocket::UDPSocket() throw(SocketException) : CommunicatingSocket(SOCK_DGRAM, IPPROTO_UDP) { + setBroadcast(); +} + +UDPSocket::UDPSocket(unsigned short localPort) throw(SocketException) : CommunicatingSocket(SOCK_DGRAM, IPPROTO_UDP) { + setLocalPort(localPort); + setBroadcast(); +} + +UDPSocket::UDPSocket(const string &localAddress, unsigned short localPort) throw(SocketException) : CommunicatingSocket(SOCK_DGRAM, IPPROTO_UDP) { + setLocalAddressAndPort(localAddress, localPort); + setBroadcast(); +} + +void UDPSocket::setBroadcast() { + // If this fails, we'll hear about it when we try to send. This will allow + // system that cannot broadcast to continue if they don't plan to broadcast + int broadcastPermission = 1; + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (raw_type *) &broadcastPermission, sizeof(broadcastPermission)); +} + +void UDPSocket::disconnect() throw(SocketException) { + sockaddr_in nullAddr; + memset(&nullAddr, 0, sizeof(nullAddr)); + nullAddr.sin_family = AF_UNSPEC; + + // Try to disconnect + if (::connect(sock, (sockaddr *) &nullAddr, sizeof(nullAddr)) < 0) { +#ifdef WIN32 + if (errno != WSAEAFNOSUPPORT) { +#else + if (errno != EAFNOSUPPORT) { +#endif + throw SocketException("Disconnect failed (connect())", true); + } + } +} + +void UDPSocket::sendTo(const void *buffer, int bufferLen, const string &foreignAddress, unsigned short foreignPort) throw(SocketException) { + sockaddr_in destAddr; + fillAddr(foreignAddress, foreignPort, destAddr); + + // Write out the whole buffer as a single message. + if (sendto(sock, (raw_type *) buffer, bufferLen, 0, (sockaddr *) &destAddr, sizeof(destAddr)) != bufferLen) { + throw SocketException("Send failed (sendto())", true); + } +} + +int UDPSocket::recvFrom(void *buffer, int bufferLen, string &sourceAddress, unsigned short &sourcePort) throw(SocketException) { + sockaddr_in clntAddr; + socklen_t addrLen = sizeof(clntAddr); + int rtn; + if ((rtn = recvfrom(sock, (raw_type *) buffer, bufferLen, 0, (sockaddr *) &clntAddr, (socklen_t *) &addrLen)) < 0) { + throw SocketException("Receive failed (recvfrom())", true); + } + sourceAddress = inet_ntoa(clntAddr.sin_addr); + sourcePort = ntohs(clntAddr.sin_port); + return rtn; +} + +void UDPSocket::setMulticastTTL(unsigned char multicastTTL) throw(SocketException) { + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (raw_type *) &multicastTTL, sizeof(multicastTTL)) < 0) { + throw SocketException("Multicast TTL set failed (setsockopt())", true); + } +} + +void UDPSocket::joinGroup(const string &multicastGroup) throw(SocketException) { + struct ip_mreq multicastRequest; + + multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastGroup.c_str()); + multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (raw_type *) &multicastRequest, sizeof(multicastRequest)) < 0) { + throw SocketException("Multicast group join failed (setsockopt())", true); + } +} + +void UDPSocket::leaveGroup(const string &multicastGroup) throw(SocketException) { + struct ip_mreq multicastRequest; + + multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastGroup.c_str()); + multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (raw_type *) &multicastRequest, sizeof(multicastRequest)) < 0) { + throw SocketException("Multicast group leave failed (setsockopt())", true); + } +} + +} // End of namespace SocketLibrary -- cgit v1.2.3