diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Socket.cc | 165 |
1 files changed, 162 insertions, 3 deletions
diff --git a/src/Socket.cc b/src/Socket.cc index 762aae3..65991a9 100644 --- a/src/Socket.cc +++ b/src/Socket.cc @@ -1,9 +1,12 @@ +#ifndef _WIN32 #include <arpa/inet.h> -#include <sys/types.h> #include <sys/socket.h> -#include <netdb.h> +#endif +#include <sys/types.h> #include <unistd.h> #include <fcntl.h> +#include <sys/stat.h> +#include <stdio.h> #include <errno.h> #include "Socket.h" #include "Threads.h" @@ -35,6 +38,139 @@ struct DNSRequest { int error; }; +static Balau::String getErrorMessage() { + Balau::String msg; +#ifdef _WIN32 + char * lpMsgBuf; + if (FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + WSAGetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL)) { + char * eol = strrchr(lpMsgBuf, '\n'); + if (eol) + *eol = 0; + msg = lpMsgBuf; + } else { + msg = "FormatMessage failed"; + } + LocalFree(lpMsgBuf); +#else + msg = strerror(errno); +#endif + return msg; +} + +#if defined(_WIN32) && !defined(AI_V4MAPPED) +// mingw32 is retarded. +#define AI_NUMERICSERV 0x00000008L +#define AI_ALL 0x00000100L +#define AI_ADDRCONFIG 0x00000400L +#define AI_V4MAPPED 0x00000800L +#define AI_NON_AUTHORITATIVE 0x00004000L +#define AI_SECURE 0x00008000L +#define AI_RETURN_PREFERRED_NAMES 0x00010000L +#define AI_FQDN 0x00020000L +#define AI_FILESERVER 0x00040000L + +// and winXP is stupid + +static const char * inet_ntop4(const unsigned char * src, char * dst, socklen_t size) { + char out[INET_ADDRSTRLEN]; + + int len = sprintf(out, "%u.%u.%u.%u", src[0], src[1], src[2], src[3]); + if (len < 0) { + return NULL; + } else if (len > size) { + errno = ENOSPC; + return NULL; + } + return strcpy(dst, out); +} + +static const char * inet_ntop6(const unsigned char * src, char * dst, socklen_t size) { + char tmp[INET6_ADDRSTRLEN], *tp; + struct { int base, len; } best, cur; + unsigned int words[8]; + int i; + + memset(words, 0, sizeof(words)); + for (i = 0; i < 16; i += 2) + words[i / 2] = (src[i] << 8) | src[i + 1]; + best.base = -1; + cur.base = -1; + for (i = 0; i < 8; i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + tp = tmp; + for (i = 0; i < 8; i++) { + if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + if (i != 0) + *tp++ = ':'; + if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4 (src + 12, tp, sizeof tmp - (tp - tmp))) + return NULL; + tp += strlen (tp); + break; + } + + int len = sprintf (tp, "%x", words[i]); + if (len < 0) + return NULL; + tp += len; + } + if (best.base != -1 && (best.base + best.len) == 8) + *tp++ = ':'; + *tp++ = '\0'; + + if ((socklen_t) (tp - tmp) > size) { + errno = ENOSPC; + return NULL; + } + + return strcpy(dst, tmp); +} + +static const char * inet_ntop(int af, const void * src, char * dst, socklen_t size) { + switch (af) { + case AF_INET: + return inet_ntop4((const unsigned char *) src, dst, size); + case AF_INET6: + return inet_ntop6((const unsigned char *) src, dst, size); + default: + errno = WSAEAFNOSUPPORT; + WSASetLastError(errno); + return NULL; + } +} + +#endif + #if 0 // TODO: use getaddrinfo_a, if available. #else @@ -100,7 +236,12 @@ Balau::Socket::Socket() throw (GeneralException) : m_fd(socket(AF_INET6, SOCK_ST Assert(m_fd >= 0); m_evtR = new SocketEvent(m_fd, EV_READ); m_evtW = new SocketEvent(m_fd, EV_WRITE); +#ifdef _WIN32 + u_long iMode = 1; + ioctlsocket(m_fd, FIONBIO, &iMode); +#else fcntl(m_fd, F_SETFL, O_NONBLOCK); +#endif memset(&m_localAddr, 0, sizeof(m_localAddr)); memset(&m_remoteAddr, 0, sizeof(m_remoteAddr)); Printer::elog(E_SOCKET, "Creating a socket at %p", this); @@ -127,7 +268,12 @@ Balau::Socket::Socket(int fd) : m_fd(fd), m_connected(true), m_connecting(false) m_evtR = new SocketEvent(m_fd, EV_READ); m_evtW = new SocketEvent(m_fd, EV_WRITE); +#ifdef _WIN32 + u_long iMode = 1; + ioctlsocket(m_fd, FIONBIO, &iMode); +#else fcntl(m_fd, F_SETFL, O_NONBLOCK); +#endif m_name.set("Socket(Connected - [%s]:%i <- [%s]:%i)", rLocal, htons(m_localAddr.sin6_port), rRemote, htons(m_remoteAddr.sin6_port)); Printer::elog(E_SOCKET, "Created a new socket from listener at %p; %s", this, m_name.to_charp()); @@ -136,6 +282,7 @@ Balau::Socket::Socket(int fd) : m_fd(fd), m_connected(true), m_connecting(false) void Balau::Socket::close() throw (GeneralException) { #ifdef _WIN32 closesocket(m_fd); + WSACleanup(); #else ::close(m_fd); #endif @@ -314,11 +461,21 @@ bool Balau::Socket::listen() { Assert(rLocal); m_name.set("Socket(Listener - [%s]:%i)", rLocal, htons(m_localAddr.sin6_port)); + Printer::elog(E_SOCKET, "Socket %i started to listen: %s", m_fd, m_name.to_charp()); + } else { + String msg = getErrorMessage(); + Printer::elog(E_SOCKET, "listen() failed with error #i (%s)", errno, msg.to_charp()); } return m_listening; } +#ifdef _WIN32 +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif +#endif + Balau::IO<Balau::Socket> Balau::Socket::accept() throw (GeneralException) { Assert(m_listening); Assert(m_fd >= 0); @@ -326,13 +483,15 @@ Balau::IO<Balau::Socket> Balau::Socket::accept() throw (GeneralException) { while(true) { sockaddr_in6 remoteAddr; socklen_t len; + Printer::elog(E_SOCKET, "Socket %i (%s) is going to accept()", m_fd, m_name.to_charp()); int s = ::accept(m_fd, (sockaddr *) &remoteAddr, &len); if (s < 0) { if ((errno == EAGAIN) || (errno == EINTR) || (errno == EWOULDBLOCK)) { Task::yield(m_evtR, true); } else { - throw GeneralException(String("Unexpected error accepting a connection: #") + errno + "(" + strerror(errno) + ")"); + String msg = getErrorMessage(); + throw GeneralException(String("Unexpected error accepting a connection: #") + errno + "(" + msg + ")"); } } else { Printer::elog(E_SOCKET, "Listener at %p got a new connection", this); |