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); | 
