|  | /* | 
|  | * Copyright 2008 Google Inc. | 
|  | * | 
|  | * Licensed 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 "Platform.h" | 
|  |  | 
|  | #include <cstdio> | 
|  | #include <cstdlib> | 
|  | #include <cstring> | 
|  | #include <cerrno> | 
|  |  | 
|  | #include "Socket.h" | 
|  |  | 
|  | void Socket::init() { | 
|  | #ifdef _WINDOWS | 
|  | // version 2.2 supported on Win95OSR2/WinNT4 and up | 
|  | WORD winsockVers = MAKEWORD(2, 2); | 
|  | WSADATA wsaData; | 
|  | int err = WSAStartup(winsockVers, &wsaData); | 
|  | if (err) { | 
|  | // TODO(jat): report error | 
|  | Debug::log(Debug::Error) << "WSAStartup(vers=2.2): err=" << err << Debug::flush; | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | bool Socket::connect(const char* host, int port) { | 
|  | Debug::log(Debug::Debugging) << "Socket::connect(host=" << host << ",port=" << port << ")" | 
|  | << Debug::flush; | 
|  | if (isConnected()) { | 
|  | Debug::log(Debug::Error) << "Socket::connect - already connected" << Debug::flush; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | SOCKETTYPE fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | 
|  | if (fd < 0) { | 
|  | Debug::log(Debug::Error) << "Socket::connect - can't get socket" << Debug::flush; | 
|  | return false; | 
|  | } | 
|  | #ifdef SO_NOSIGPIPE | 
|  | // On BSD, we need to suppress the SIGPIPE if the remote end disconnects. | 
|  | int option_value = 1; | 
|  | if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &option_value, sizeof(int))) { | 
|  | Debug::log(Debug::Error) << "Socket::connect - can't set NOSIGPIPE option" << Debug::flush; | 
|  | return false; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | struct sockaddr_in sockAddr; | 
|  | memset(&sockAddr, 0, static_cast<int>(sizeof(sockAddr))); | 
|  | // check for numeric IP4 addresses first | 
|  | // TODO(jat): handle IPv6 addresses | 
|  | unsigned long numericAddr = inet_addr(host); | 
|  | if (numericAddr != 0xFFFFFFFF) { | 
|  | sockAddr.sin_addr.s_addr = numericAddr; | 
|  | sockAddr.sin_family = AF_INET; | 
|  | } else { | 
|  | struct hostent* hent = gethostbyname(host); | 
|  | if (!hent || !hent->h_addr_list[0]) { | 
|  | Debug::log(Debug::Error) << "Unable to get address for " << host << Debug::flush; | 
|  | return false; | 
|  | } | 
|  | memcpy(&(sockAddr.sin_addr), hent->h_addr_list[0], hent->h_length); | 
|  | sockAddr.sin_family = hent->h_addrtype; | 
|  | } | 
|  | sockAddr.sin_port = htons(port); | 
|  |  | 
|  | if (::connect(fd, (struct sockaddr*) &sockAddr, sizeof(sockAddr)) < 0) { | 
|  | #ifdef _WINDOWS | 
|  | char buf[256]; | 
|  | DWORD dwLastError = ::GetLastError(); | 
|  | strerror_s(buf, sizeof(buf), dwLastError); | 
|  | Debug::log(Debug::Error) << "Failed to connect to " << host << ":" << port << " -- error code " | 
|  | << dwLastError << Debug::flush; | 
|  | closesocket(fd); | 
|  | ::SetLastError(dwLastError); | 
|  | #else | 
|  | Debug::log(Debug::Error) << "Can't connect to " << host << ":" << port << " -- " | 
|  | << strerror(errno) << Debug::flush; | 
|  | close(fd); | 
|  | #endif | 
|  | return false; | 
|  | } | 
|  | sock = fd; | 
|  | connected = true; | 
|  | readBufPtr = readValid = readBuf; | 
|  | writeBufPtr = writeBuf; | 
|  | #ifdef _WINDOWS | 
|  | Debug::log(Debug::Spam) << "  connected" << Debug::flush; | 
|  | #else | 
|  | Debug::log(Debug::Spam) << "  connected, fd=" << fd << Debug::flush; | 
|  | #endif | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Socket::disconnect(bool doFlush) { | 
|  | if (connected) { | 
|  | Debug::log(Debug::Debugging) << "Disconnecting socket" << Debug::flush; | 
|  | if (doFlush) { | 
|  | flush(); | 
|  | } | 
|  | connected = false; | 
|  | #ifdef _WINDOWS | 
|  | closesocket(sock); | 
|  | #else | 
|  | shutdown(sock, SHUT_RDWR); | 
|  | close(sock); | 
|  | #endif | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Socket::emptyWriteBuf() { | 
|  | size_t len = writeBufPtr - writeBuf; | 
|  | Debug::log(Debug::Spam) << "Socket::emptyWriteBuf: len=" << len << Debug::flush; | 
|  | ++numWrites; | 
|  | totWriteBytes += len; | 
|  | if (len > maxWriteBytes) { | 
|  | maxWriteBytes = len; | 
|  | } | 
|  | for (char* ptr = writeBuf; len > 0; ) { | 
|  | ssize_t n = send(sock, ptr, len, 0); | 
|  | if (n <= 0) { | 
|  | if (errno == EPIPE) { | 
|  | Debug::log(Debug::Warning) << "Other end of socket disconnected" << Debug::flush; | 
|  | disconnect(false); | 
|  | return false; | 
|  | } | 
|  | Debug::log(Debug::Error) << "Error " << errno << " writing " << len << " bytes to socket" | 
|  | << Debug::flush; | 
|  | return false; | 
|  | } | 
|  | ptr += n; | 
|  | len -= n; | 
|  | } | 
|  | writeBufPtr = writeBuf; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Socket::fillReadBuf() { | 
|  | readBufPtr = readBuf; | 
|  | errno = 0; | 
|  | ssize_t n = recv(sock, readBuf, BUF_SIZE, 0); | 
|  | if (n <= 0) { | 
|  | // EOF results in no error | 
|  | if (!errno || errno == EPIPE) { | 
|  | Debug::log(Debug::Warning) << "Other end of socket disconnected" << Debug::flush; | 
|  | disconnect(false); | 
|  | return false; | 
|  | } | 
|  | Debug::log(Debug::Error) << "Error " << errno << " reading " << BUF_SIZE << " bytes from socket" | 
|  | << Debug::flush; | 
|  | return false; | 
|  | } | 
|  | ++numReads; | 
|  | totReadBytes += n; | 
|  | if (static_cast<size_t>(n) > maxReadBytes) { | 
|  | maxReadBytes = n; | 
|  | } | 
|  | readValid = readBuf + n; | 
|  | return true; | 
|  | } |