| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| |
| #include "qpid/sys/Socket.h" |
| #include "IoHandlePrivate.h" |
| #include "check.h" |
| #include "qpid/sys/Time.h" |
| |
| #include <cstdlib> |
| #include <string.h> |
| #include <iostream> |
| #include <memory.h> |
| |
| #include <winsock2.h> |
| #include <ws2tcpip.h> |
| |
| #include <boost/format.hpp> |
| |
| // Need to initialize WinSock. Ideally, this would be a singleton or embedded |
| // in some one-time initialization function. I tried boost singleton and could |
| // not get it to compile (and others located in google had the same problem). |
| // So, this simple static with an interlocked increment will do for known |
| // use cases at this time. Since this will only shut down winsock at process |
| // termination, there may be some problems with client programs that also |
| // expect to load and unload winsock, but we'll see... |
| // If someone does get an easy-to-use singleton sometime, converting to it |
| // may be preferable. |
| |
| namespace { |
| |
| static LONG volatile initialized = 0; |
| |
| class WinSockSetup { |
| // : public boost::details::pool::singleton_default<WinSockSetup> { |
| |
| public: |
| WinSockSetup() { |
| LONG timesEntered = InterlockedIncrement(&initialized); |
| if (timesEntered > 1) |
| return; |
| err = 0; |
| WORD wVersionRequested; |
| WSADATA wsaData; |
| |
| /* Request WinSock 2.2 */ |
| wVersionRequested = MAKEWORD(2, 2); |
| err = WSAStartup(wVersionRequested, &wsaData); |
| } |
| |
| ~WinSockSetup() { |
| WSACleanup(); |
| } |
| |
| public: |
| int error(void) const { return err; } |
| |
| protected: |
| DWORD err; |
| }; |
| |
| static WinSockSetup setup; |
| |
| } /* namespace */ |
| |
| namespace qpid { |
| namespace sys { |
| |
| namespace { |
| |
| std::string getName(SOCKET fd, bool local, bool includeService = false) |
| { |
| sockaddr_in name; // big enough for any socket address |
| socklen_t namelen = sizeof(name); |
| if (local) { |
| QPID_WINSOCK_CHECK(::getsockname(fd, (sockaddr*)&name, &namelen)); |
| } else { |
| QPID_WINSOCK_CHECK(::getpeername(fd, (sockaddr*)&name, &namelen)); |
| } |
| |
| char servName[NI_MAXSERV]; |
| char dispName[NI_MAXHOST]; |
| if (includeService) { |
| if (int rc = ::getnameinfo((sockaddr*)&name, namelen, |
| dispName, sizeof(dispName), |
| servName, sizeof(servName), |
| NI_NUMERICHOST | NI_NUMERICSERV) != 0) |
| throw qpid::Exception(QPID_MSG(gai_strerror(rc))); |
| return std::string(dispName) + ":" + std::string(servName); |
| } else { |
| if (int rc = ::getnameinfo((sockaddr*)&name, namelen, |
| dispName, sizeof(dispName), |
| 0, 0, |
| NI_NUMERICHOST) != 0) |
| throw qpid::Exception(QPID_MSG(gai_strerror(rc))); |
| return dispName; |
| } |
| } |
| |
| std::string getService(SOCKET fd, bool local) |
| { |
| sockaddr_in name; // big enough for any socket address |
| socklen_t namelen = sizeof(name); |
| |
| if (local) { |
| QPID_WINSOCK_CHECK(::getsockname(fd, (sockaddr*)&name, &namelen)); |
| } else { |
| QPID_WINSOCK_CHECK(::getpeername(fd, (sockaddr*)&name, &namelen)); |
| } |
| |
| char servName[NI_MAXSERV]; |
| if (int rc = ::getnameinfo((sockaddr*)&name, namelen, |
| 0, 0, |
| servName, sizeof(servName), |
| NI_NUMERICHOST | NI_NUMERICSERV) != 0) |
| throw qpid::Exception(QPID_MSG(gai_strerror(rc))); |
| return servName; |
| } |
| } // namespace |
| |
| Socket::Socket() : |
| IOHandle(new IOHandlePrivate) |
| { |
| createTcp(); |
| } |
| |
| Socket::Socket(IOHandlePrivate* h) : |
| IOHandle(h) |
| {} |
| |
| void Socket::createTcp() const |
| { |
| SOCKET& socket = impl->fd; |
| if (socket != INVALID_SOCKET) Socket::close(); |
| SOCKET s = ::socket (PF_INET, SOCK_STREAM, 0); |
| if (s == INVALID_SOCKET) throw QPID_WINDOWS_ERROR(WSAGetLastError()); |
| socket = s; |
| } |
| |
| void Socket::setTimeout(const Duration& interval) const |
| { |
| const SOCKET& socket = impl->fd; |
| int64_t nanosecs = interval; |
| nanosecs /= (1000 * 1000); // nsecs -> usec -> msec |
| int msec = 0; |
| if (nanosecs > std::numeric_limits<int>::max()) |
| msec = std::numeric_limits<int>::max(); |
| else |
| msec = static_cast<int>(nanosecs); |
| setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&msec, sizeof(msec)); |
| setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&msec, sizeof(msec)); |
| } |
| |
| void Socket::setNonblocking() const { |
| u_long nonblock = 1; |
| QPID_WINSOCK_CHECK(ioctlsocket(impl->fd, FIONBIO, &nonblock)); |
| } |
| |
| void Socket::connect(const std::string& host, uint16_t port) const |
| { |
| std::stringstream portstream; |
| portstream << port << std::ends; |
| std::string portstr = portstream.str(); |
| std::stringstream namestream; |
| namestream << host << ":" << port; |
| connectname = namestream.str(); |
| |
| const SOCKET& socket = impl->fd; |
| // TODO: Be good to make this work for IPv6 as well as IPv4. Would require |
| // other changes, such as waiting to create the socket until after we |
| // have the address family. Maybe unbundle the translation of names here; |
| // use TcpAddress to resolve things and make this class take a TcpAddress |
| // and grab its address family to create the socket. |
| struct addrinfo hints; |
| memset(&hints, 0, sizeof(hints)); |
| hints.ai_family = AF_INET; // We always creating AF_INET-only sockets. |
| hints.ai_socktype = SOCK_STREAM; // We always do TCP |
| addrinfo *addrs; |
| int status = getaddrinfo(host.c_str(), portstr.c_str(), &hints, &addrs); |
| if (status != 0) |
| throw Exception(QPID_MSG("Cannot resolve " << host << ": " << |
| gai_strerror(status))); |
| addrinfo *addr = addrs; |
| int error = 0; |
| WSASetLastError(0); |
| while (addr != 0) { |
| if ((::connect(socket, addr->ai_addr, addr->ai_addrlen) == 0) || |
| (WSAGetLastError() == WSAEWOULDBLOCK)) |
| break; |
| // Error... save this error code and see if there are other address |
| // to try before throwing the exception. |
| error = WSAGetLastError(); |
| addr = addr->ai_next; |
| } |
| freeaddrinfo(addrs); |
| if (error) |
| throw qpid::Exception(QPID_MSG(strError(error) << ": " << connectname)); |
| } |
| |
| void |
| Socket::close() const |
| { |
| SOCKET& socket = impl->fd; |
| if (socket == INVALID_SOCKET) return; |
| QPID_WINSOCK_CHECK(closesocket(socket)); |
| socket = INVALID_SOCKET; |
| } |
| |
| |
| int Socket::write(const void *buf, size_t count) const |
| { |
| const SOCKET& socket = impl->fd; |
| int sent = ::send(socket, (const char *)buf, count, 0); |
| if (sent == SOCKET_ERROR) |
| return -1; |
| return sent; |
| } |
| |
| int Socket::read(void *buf, size_t count) const |
| { |
| const SOCKET& socket = impl->fd; |
| int received = ::recv(socket, (char *)buf, count, 0); |
| if (received == SOCKET_ERROR) |
| return -1; |
| return received; |
| } |
| |
| int Socket::listen(uint16_t port, int backlog) const |
| { |
| const SOCKET& socket = impl->fd; |
| BOOL yes=1; |
| QPID_WINSOCK_CHECK(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes))); |
| struct sockaddr_in name; |
| memset(&name, 0, sizeof(name)); |
| name.sin_family = AF_INET; |
| name.sin_port = htons(port); |
| name.sin_addr.s_addr = 0; |
| if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) == SOCKET_ERROR) |
| throw Exception(QPID_MSG("Can't bind to port " << port << ": " << strError(WSAGetLastError()))); |
| if (::listen(socket, backlog) == SOCKET_ERROR) |
| throw Exception(QPID_MSG("Can't listen on port " << port << ": " << strError(WSAGetLastError()))); |
| |
| socklen_t namelen = sizeof(name); |
| QPID_WINSOCK_CHECK(::getsockname(socket, (struct sockaddr*)&name, &namelen)); |
| return ntohs(name.sin_port); |
| } |
| |
| Socket* Socket::accept(struct sockaddr *addr, socklen_t *addrlen) const |
| { |
| SOCKET afd = ::accept(impl->fd, addr, addrlen); |
| if (afd != INVALID_SOCKET) |
| return new Socket(new IOHandlePrivate(afd)); |
| else if (WSAGetLastError() == EAGAIN) |
| return 0; |
| else throw QPID_WINDOWS_ERROR(WSAGetLastError()); |
| } |
| |
| std::string Socket::getSockname() const |
| { |
| return getName(impl->fd, true); |
| } |
| |
| std::string Socket::getPeername() const |
| { |
| return getName(impl->fd, false); |
| } |
| |
| std::string Socket::getPeerAddress() const |
| { |
| if (!connectname.empty()) |
| return std::string (connectname); |
| return getName(impl->fd, false, true); |
| } |
| |
| std::string Socket::getLocalAddress() const |
| { |
| return getName(impl->fd, true, true); |
| } |
| |
| uint16_t Socket::getLocalPort() const |
| { |
| return atoi(getService(impl->fd, true).c_str()); |
| } |
| |
| uint16_t Socket::getRemotePort() const |
| { |
| return atoi(getService(impl->fd, true).c_str()); |
| } |
| |
| int Socket::getError() const |
| { |
| int result; |
| socklen_t rSize = sizeof (result); |
| |
| QPID_WINSOCK_CHECK(::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, (char *)&result, &rSize)); |
| return result; |
| } |
| |
| void Socket::setTcpNoDelay(bool nodelay) const |
| { |
| if (nodelay) { |
| int flag = 1; |
| int result = setsockopt(impl->fd, |
| IPPROTO_TCP, |
| TCP_NODELAY, |
| (char *)&flag, |
| sizeof(flag)); |
| QPID_WINSOCK_CHECK(result); |
| } |
| } |
| |
| }} // namespace qpid::sys |