/* * Baltisot * Copyright (C) 1999-2008 Nicolas "Pixel" Noble * * 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 2 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 */ #ifdef _WIN32 #include #endif #define _BSD_SOCKLEN_T_ int #ifndef _WIN32 #include #include #include #include #include #endif #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "BString.h" #include "Socket.h" #include "Exceptions.h" #include "Input.h" #include "Output.h" #include "gettext.h" #ifdef _WIN32 class dummy_win32_socket_class_t : public Base { public: dummy_win32_socket_class_t() throw (GeneralException) { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) throw GeneralException("WSAStartup() failed."); } } dummy_win32_socket_class; typedef int socklen_t; #endif Socket::Socket() throw (GeneralException) : Handle(socket(AF_INET, SOCK_STREAM, 0)), connected(false), listening(false), writeclosed(false), readclosed(false), connecting(false), pair(-1) { // cerr << "Socket(): connected = " << connected << "; readclosed = " << readclosed << "; writeclosed = " << writeclosed << endl; if (GetHandle() < 0) { throw GeneralException(_("Error creating socket.")); } } Socket::Socket(const Socket & s) : Handle(s), connected(s.connected), listening(s.listening), writeclosed(s.writeclosed), readclosed(s.readclosed), connecting(s.connecting), pair(-1) { // cerr << "Constructing a socket by copy...\n"; // cerr << "Socket(const Socket &): connected = " << connected << "; readclosed = " << readclosed << "; writeclosed = " << writeclosed << endl; } Socket::Socket(int h, int p) : Handle(h), connected(true), listening(false), writeclosed(false), readclosed(false), connecting(false), pair(p) { } Socket Socket::CreatePair() throw (GeneralException) { int socks[2]; #ifdef _WIN32 struct sockaddr_in addr; SOCKET listener; int e; int addrlen = sizeof(addr); DWORD flags = WSA_FLAG_OVERLAPPED; socks[0] = socks[1] = INVALID_SOCKET; if ((listener = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) throw GeneralException("WIN32 SocketPair: couldn't create the initial listener."); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(0x7f000001); addr.sin_port = 0; e = bind(listener, (const struct sockaddr*) &addr, sizeof(addr)); if (e == SOCKET_ERROR) { e = WSAGetLastError(); closesocket(listener); WSASetLastError(e); throw GeneralException("WIN32 SocketPair: couldn't bind the initial listener."); } e = getsockname(listener, (struct sockaddr*) &addr, &addrlen); if (e == SOCKET_ERROR) { e = WSAGetLastError(); closesocket(listener); WSASetLastError(e); throw GeneralException("WIN32 SocketPair: couldn't get the port number of the initial listener."); } do { if (listen(listener, 1) == SOCKET_ERROR) break; if ((socks[0] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, flags)) == INVALID_SOCKET) break; if (connect(socks[0], (const struct sockaddr*) &addr, sizeof(addr)) == SOCKET_ERROR) break; if ((socks[1] = accept(listener, NULL, NULL)) == INVALID_SOCKET) break; closesocket(listener); return Socket(socks[0], socks[1]); } while (0); e = WSAGetLastError(); closesocket(listener); closesocket(socks[0]); closesocket(socks[1]); WSASetLastError(e); throw GeneralException("WIN32 SocketPair: error during creation..."); #else if (socketpair(AF_LOCAL, SOCK_STREAM, 0, socks) == -1) { throw GeneralException("Couldn't create a socket pair..."); } return Socket(socks[0], socks[1]); #endif } Socket Socket::GetPair() throw (GeneralException) { if (pair != -1) { int p = pair; pair = -1; return Socket(p); } throw GeneralException("Not a paired socket."); } String Socket::GetName(void) { return String("socket"); } bool Socket::IsConnected(void) { return connected; } bool Socket::IsConnecting(void) { return connecting; } bool Socket::IsListening(void) { return listening; } bool Socket::CanRead(void) { // cerr << "CanRead: connected = " << connected << "; readclosed = " << readclosed << endl; return connected && !readclosed; } bool Socket::CanWrite(void) { // cerr << "CanWrite: connected = " << connected << "; writeclosed = " << writeclosed << endl; return connected && !writeclosed; } void Socket::CloseWrite(void) { if (!writeclosed) { writeclosed = true; shutdown(GetHandle(), 1); } } void Socket::CloseRead(void) { if (!readclosed) { readclosed = true; shutdown(GetHandle(), 0); } } /***********************************************\ * Toute la suite n'est pas à décrire. Consulter * * plutôt un document décrivant les sockets. * \***********************************************/ bool Socket::SetLocal(const String & vhost, int port) { struct hostent * localhostent; struct in_addr localhostaddr; struct sockaddr_in localsocketaddr; memset((void *)&localhostaddr, 0, sizeof(localhostaddr)); if (vhost.strlen() != 0) { if ((localhostent = gethostbyname(vhost.to_charp()))) { memcpy((void *)&localhostaddr, localhostent->h_addr, sizeof(localhostaddr)); } else { return false; } } else { localhostaddr.s_addr = htonl(INADDR_ANY); } memset(&localsocketaddr, 0, sizeof(struct sockaddr_in)); localsocketaddr.sin_family = AF_INET; localsocketaddr.sin_addr = localhostaddr; localsocketaddr.sin_port = htons(port); #ifndef _WIN32 int enable = 1; setsockopt(GetHandle(), SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); #endif if (bind(GetHandle(), (struct sockaddr *) &localsocketaddr, sizeof(localsocketaddr)) < 0) { return false; } else { return true; } } bool Socket::Connect(const String & host, int port) { struct hostent * remotehostent; struct sockaddr_in remotesocketaddr; if (!listening && !connected) { // std::cerr << " - Resolving '" << host << "'..." << std::endl; if (!(remotehostent = gethostbyname(host.to_charp()))) { return false; } remotesocketaddr.sin_family = AF_INET; remotesocketaddr.sin_port = htons(port); bcopy(remotehostent->h_addr, &remotesocketaddr.sin_addr, remotehostent->h_length); // std::cerr << " - Connecting to port " << port << " ..." << std::endl; if (!connect(GetHandle(), (struct sockaddr *)&remotesocketaddr, sizeof(remotesocketaddr))) { // std::cerr << " - Connected." << std::endl; connected = true; } else { // std::cerr << " - Error connecting: " << strerror(errno) << std::endl; #ifdef _WIN32 if (WSAGetLastError() == WSAEWOULDBLOCK) { #else if (errno == EINPROGRESS) { #endif connecting = true; } } } return connected; } bool Socket::Listen(void) { if (!listening && !connected) { if (!listen(GetHandle(), 10)) { listening = true; } } return listening; } Socket Socket::Accept(void) throw (GeneralException) { struct sockaddr inaddr; socklen_t inlen = sizeof(inaddr); int h; if ((h = accept(GetHandle(), &inaddr, &inlen)) < 0) { throw GeneralException(_("Failed accepting.")); } else { return Socket(h); } } int Socket::GetPort() { int r; struct sockaddr_in localsocketaddr; socklen_t locallen = sizeof(localsocketaddr); if (getsockname(GetHandle(), (struct sockaddr *) &localsocketaddr, &locallen)) { return -1; } r = ntohs(localsocketaddr.sin_port); return r; } Uint32 Socket::GetAddr() { Uint32 r; struct sockaddr_in localsocketaddr; socklen_t locallen = sizeof(localsocketaddr); if (getsockname(GetHandle(), (struct sockaddr *) &localsocketaddr, &locallen)) { return -1; } r = ntohl(localsocketaddr.sin_addr.s_addr); return r; } int Socket::GetDistantPort() { int r; struct sockaddr_in distantsockaddr; socklen_t distantlen = sizeof(distantsockaddr); if (getpeername(GetHandle(), (struct sockaddr *) &distantsockaddr, &distantlen)) { return -1; } r = ntohs(distantsockaddr.sin_port); return r; } Uint32 Socket::GetDistantAddr() { Uint32 r; struct sockaddr_in distantsockaddr; socklen_t distantlen = sizeof(distantsockaddr); if (getpeername(GetHandle(), (struct sockaddr *) &distantsockaddr, &distantlen)) { return -1; } r = ntohl(distantsockaddr.sin_addr.s_addr); return r; } bool Socket::FinalizeConnect() { int r; socklen_t l = sizeof(r); if (getsockopt(GetHandle(), SOL_SOCKET, SO_ERROR, (char *)&r, &l) == 0) { if (r == 0) { connected = true; } } connecting = false; return connected; } ssize_t Socket::nwrite(const void * buf, size_t count) throw (GeneralException) { return ::send(GetHandle(), (const char *) buf, count, 0); } ssize_t Socket::nread(void * buf, size_t count) throw (GeneralException) { return ::recv(GetHandle(), (char *) buf, count, 0); } int Socket::ndup() const throw (GeneralException) { #ifdef _WIN32 WSAPROTOCOL_INFO ProtocolInfo; int r, s; if (r = WSADuplicateSocket(GetHandle(), GetCurrentProcessId(), &ProtocolInfo)) { throw GeneralException("Error within WSADuplicateSocket()."); } if ((s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, &ProtocolInfo, 0, 0)) == INVALID_SOCKET) { throw GeneralException("Error within WSASocket() while dup()ing it."); } return s; #else return dup(GetHandle()); #endif } int Socket::nclose() throw (GeneralException) { #ifdef _WIN32 return closesocket(GetHandle()); #else return Handle::nclose(); #endif }