/* * Copyright (C) 2005 The Android Open Source Project * * 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. */ // // Internet address class. // #ifdef HAVE_WINSOCK // This needs to come first, or Cygwin gets concerned about a potential // clash between WinSock and <sys/types.h>. # include <winsock2.h> #endif #include <utils/Socket.h> #include <utils/inet_address.h> #include <utils/Log.h> #include <utils/Timers.h> #ifndef HAVE_WINSOCK # include <sys/types.h> # include <sys/socket.h> # include <netinet/in.h> # include <arpa/inet.h> #endif #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <assert.h> using namespace android; /* * =========================================================================== * Socket * =========================================================================== */ #ifndef INVALID_SOCKET # define INVALID_SOCKET (-1) #endif #define UNDEF_SOCKET ((unsigned long) INVALID_SOCKET) /*static*/ bool Socket::mBootInitialized = false; /* * Extract system-dependent error code. */ static inline int getSocketError(void) { #ifdef HAVE_WINSOCK return WSAGetLastError(); #else return errno; #endif } /* * One-time initialization for socket code. */ /*static*/ bool Socket::bootInit(void) { #ifdef HAVE_WINSOCK WSADATA wsaData; int err; err = WSAStartup(MAKEWORD(2, 0), &wsaData); if (err != 0) { LOG(LOG_ERROR, "socket", "Unable to start WinSock\n"); return false; } LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n", LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); #endif mBootInitialized = true; return true; } /* * One-time shutdown for socket code. */ /*static*/ void Socket::finalShutdown(void) { #ifdef HAVE_WINSOCK WSACleanup(); #endif mBootInitialized = false; } /* * Simple constructor. Allow the application to create us and then make * bind/connect calls. */ Socket::Socket(void) : mSock(UNDEF_SOCKET) { if (!mBootInitialized) LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n"); } /* * Destructor. Closes the socket and resets our storage. */ Socket::~Socket(void) { close(); } /* * Create a socket and connect to the specified host and port. */ int Socket::connect(const char* host, int port) { if (mSock != UNDEF_SOCKET) { LOG(LOG_WARN, "socket", "Socket already connected\n"); return -1; } InetSocketAddress sockAddr; if (!sockAddr.create(host, port)) return -1; //return doConnect(sockAddr); int foo; foo = doConnect(sockAddr); return foo; } /* * Create a socket and connect to the specified host and port. */ int Socket::connect(const InetAddress* addr, int port) { if (mSock != UNDEF_SOCKET) { LOG(LOG_WARN, "socket", "Socket already connected\n"); return -1; } InetSocketAddress sockAddr; if (!sockAddr.create(addr, port)) return -1; return doConnect(sockAddr); } /* * Finish creating a socket by connecting to the remote host. * * Returns 0 on success. */ int Socket::doConnect(const InetSocketAddress& sockAddr) { #ifdef HAVE_WINSOCK SOCKET sock; #else int sock; #endif const InetAddress* addr = sockAddr.getAddress(); int port = sockAddr.getPort(); struct sockaddr_in inaddr; DurationTimer connectTimer; assert(sizeof(struct sockaddr_in) == addr->getAddressLength()); memcpy(&inaddr, addr->getAddress(), addr->getAddressLength()); inaddr.sin_port = htons(port); //fprintf(stderr, "--- connecting to %s:%d\n", // sockAddr.getHostName(), port); sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == INVALID_SOCKET) { int err = getSocketError(); LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err); return (err != 0) ? err : -1; } connectTimer.start(); if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) { int err = getSocketError(); LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n", sockAddr.getHostName(), port, err); return (err != 0) ? err : -1; } connectTimer.stop(); if ((long) connectTimer.durationUsecs() > 100000) { LOG(LOG_INFO, "socket", "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(), port, ((long) connectTimer.durationUsecs()) / 1000000.0); } mSock = (unsigned long) sock; LOG(LOG_VERBOSE, "socket", "--- connected to %s:%d\n", sockAddr.getHostName(), port); return 0; } /* * Close the socket if it needs closing. */ bool Socket::close(void) { if (mSock != UNDEF_SOCKET) { //fprintf(stderr, "--- closing socket %lu\n", mSock); #ifdef HAVE_WINSOCK if (::closesocket((SOCKET) mSock) != 0) return false; #else if (::close((int) mSock) != 0) return false; #endif } mSock = UNDEF_SOCKET; return true; } /* * Read data from socket. * * Standard semantics: read up to "len" bytes into "buf". Returns the * number of bytes read, or less than zero on error. */ int Socket::read(void* buf, ssize_t len) const { if (mSock == UNDEF_SOCKET) { LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n"); return -500; } #ifdef HAVE_WINSOCK SOCKET sock = (SOCKET) mSock; #else int sock = (int) mSock; #endif int cc; cc = recv(sock, (char*)buf, len, 0); if (cc < 0) { int err = getSocketError(); return (err > 0) ? -err : -1; } return cc; } /* * Write data to a socket. * * Standard semantics: write up to "len" bytes into "buf". Returns the * number of bytes written, or less than zero on error. */ int Socket::write(const void* buf, ssize_t len) const { if (mSock == UNDEF_SOCKET) { LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n"); return -500; } #ifdef HAVE_WINSOCK SOCKET sock = (SOCKET) mSock; #else int sock = (int) mSock; #endif int cc; cc = send(sock, (const char*)buf, len, 0); if (cc < 0) { int err = getSocketError(); return (err > 0) ? -err : -1; } return cc; } /* * =========================================================================== * Socket tests * =========================================================================== */ /* * Read all data from the socket. The data is read into a buffer that * expands as needed. * * On exit, the buffer is returned, and the length of the data is stored * in "*sz". A null byte is added to the end, but is not included in * the length. */ static char* socketReadAll(const Socket& s, int *sz) { int max, r; char *data, *ptr, *tmp; data = (char*) malloc(max = 32768); if (data == NULL) return NULL; ptr = data; for (;;) { if ((ptr - data) == max) { tmp = (char*) realloc(data, max *= 2); if(tmp == 0) { free(data); return 0; } } r = s.read(ptr, max - (ptr - data)); if (r == 0) break; if (r < 0) { LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r); break; } ptr += r; } if ((ptr - data) == max) { tmp = (char*) realloc(data, max + 1); if (tmp == NULL) { free(data); return NULL; } } *ptr = '\0'; *sz = (ptr - data); return data; } /* * Exercise the Socket class. */ void android::TestSockets(void) { printf("----- SOCKET TEST ------\n"); Socket::bootInit(); char* buf = NULL; int len, cc; const char* kTestStr = "GET / HTTP/1.0\n" "Connection: close\n" "\n"; Socket sock; if (sock.connect("www.google.com", 80) != 0) { fprintf(stderr, "socket connected failed\n"); goto bail; } cc = sock.write(kTestStr, strlen(kTestStr)); if (cc != (int) strlen(kTestStr)) { fprintf(stderr, "write failed, res=%d\n", cc); goto bail; } buf = socketReadAll(sock, &len); printf("GOT '%s'\n", buf); bail: sock.close(); free(buf); }