Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members  

SocketDescriptor.cc

Go to the documentation of this file.
00001 /**************************************************************************
00002  Copyright (c) 2000-2001, Tony Garnock-Jones
00003  All rights reserved.
00004 
00005  Redistribution and use in source and binary forms, with or without
00006  modification, are permitted provided that the following conditions are
00007  met:
00008 
00009      * Redistributions of source code must retain the above copyright
00010        notice, this list of conditions and the following disclaimer.
00011 
00012      * Redistributions in binary form must reproduce the above
00013        copyright notice, this list of conditions and the following
00014        disclaimer in the documentation and/or other materials provided
00015        with the distribution.
00016 
00017      * Neither the names of the copyright holders nor the names of this
00018        software's contributors may be used to endorse or promote
00019        products derived from this software without specific prior
00020        written permission.
00021 
00022  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00025  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
00026  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00027  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00028  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00029  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00030  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00031  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00032  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 **************************************************************************/
00034 
00035 #include <stdlib.h>
00036 #include <string.h>
00037 #include <stdio.h>
00038 
00039 #include <sys/types.h>
00040 #include <sys/time.h>
00041 #include <sys/stat.h>
00042 #include <fcntl.h>
00043 #include <unistd.h>
00044 #include <errno.h>
00045 
00046 #ifdef __WIN32__
00047 #include <winsock.h>
00048 #else
00049 #include <sys/socket.h>
00050 #include <netdb.h>
00051 #include <netinet/in.h>
00052 #endif
00053 
00054 #include <FileDescriptor.h>
00055 #include <SocketDescriptor.h>
00056 
00057 #if defined(hpux) || defined(__WIN32__)
00058 /* Hmm. Not strictly true. Curse socklen_t to the nether regions of hell. */
00059 /* And Windows. */
00060 typedef int socklen_t;
00061 #endif
00062 
00063 ///////////////////////////////////////////////////////////////////////////
00064 
00065 #ifdef __WIN32__
00066 
00067 class Win32SocketManager {
00068 public:
00069   Win32SocketManager() {
00070     WSADATA wsaData;
00071     int result = WSAStartup(1, &wsaData);
00072     if (result) {
00073       fprintf(stderr, "Could not initialise winsock (WSAStartup returned %d)\n",
00074               result);
00075       exit(1);
00076     }
00077   }
00078 
00079   ~Win32SocketManager() {
00080     WSACleanup();
00081   }
00082 };
00083 
00084 static Win32SocketManager win32SocketManager;
00085 
00086 #endif  /* defined(__WIN32__) */
00087 
00088 ///////////////////////////////////////////////////////////////////////////
00089 
00090 int SocketDescriptor::lowlevel_read(int fd, char *buf, int len) {
00091 #ifdef __WIN32__
00092   int result = ::recv(getFd(), buf, len, 0);
00093 
00094   if (result != SOCKET_ERROR) {
00095     return result;
00096   }
00097 
00098   int sockerrno = WSAGetLastError();
00099   switch (sockerrno) {
00100     case WSAECONNRESET:
00101       // Effectively an end-of-file.
00102       return 0;
00103 
00104     case WSAEWOULDBLOCK:
00105       // Hmm. Sockets on Windows appear to indicate partial-data-availability
00106       // by returning nothing less than what you asked for.
00107       //
00108       // "Interesting" hack to make FileDescriptor::fillBuffer() return false.
00109       errno = EAGAIN;
00110       return -1;
00111 
00112     default:
00113       fprintf(stderr, "SocketDescriptor::lowlevel_read: Got WSAGetLastError of %d (0x%x)\n",
00114               sockerrno, sockerrno);
00115       return 0;
00116   }
00117 
00118 #else
00119   return ::read(getFd(), buf, len);
00120 #endif
00121 }
00122 
00123 int SocketDescriptor::lowlevel_write(int fd, char const *buf, int len) {
00124 #ifdef linux
00125   return ::send(getFd(), buf, len, MSG_NOSIGNAL);
00126 #else
00127 # ifdef __WIN32__
00128   return ::send(getFd(), buf, len, 0);
00129 # else
00130   return ::write(getFd(), buf, len);
00131 # endif
00132 #endif
00133 }
00134 
00135 #ifdef __WIN32__
00136 int SocketDescriptor::lowlevel_close(int fd) {
00137   return ::closesocket(fd);
00138 }
00139 #endif
00140 
00141 void SocketDescriptor::close() {
00142   shutdown(getFd(), 2);
00143   FileDescriptor::close();
00144 }
00145 
00146 ///////////////////////////////////////////////////////////////////////////
00147 
00148 static bool getAddrByName(string const &name, unsigned int &addr) {
00149 #ifdef __WIN32__
00150   // Teach the windows resolver about dotted-quads.
00151   unsigned long dottedAddr = inet_addr(name.c_str());
00152   if (dottedAddr != INADDR_NONE) {
00153     addr = dottedAddr;
00154     return true;
00155   }
00156 #endif
00157 
00158   struct hostent *he = gethostbyname(name.c_str());
00159 
00160   if (he == NULL)
00161     return false;
00162 
00163   addr = * (int *) he->h_addr_list[0];
00164   return true;
00165 }
00166 
00167 static string getNameByAddr(char const *addr) {
00168   struct hostent *h = gethostbyaddr(addr, 4, AF_INET);
00169 
00170   if (h == NULL) {
00171     char buf[20];
00172     sprintf(buf, "%u.%u.%u.%u",
00173             ((unsigned char *) addr)[0],
00174             ((unsigned char *) addr)[1],
00175             ((unsigned char *) addr)[2],
00176             ((unsigned char *) addr)[3]);
00177     return buf;
00178   }
00179 
00180   return h->h_name;
00181 }
00182 
00183 ///////////////////////////////////////////////////////////////////////////
00184 
00185 ClientSocketDescriptor::ClientSocketDescriptor(ref<FileDescriptorManager> const &_fdMgr,
00186                                                string const &_hostname, int _port)
00187   : SocketDescriptor(_fdMgr, socket(PF_INET, SOCK_STREAM, 0))
00188 {
00189   if (!isOpen())
00190     throw Exception("Could not create socket in ClientSocketDescriptor ctor", errno);
00191 
00192   int sock = getFd();
00193 
00194   setRemoteAddr(_hostname, _port);
00195 
00196   unsigned int connectaddr;
00197   if (!getAddrByName(_hostname, connectaddr)) {
00198     close();
00199     throw Exception("Host " + _hostname + " not found in ClientSocketDescriptor ctor");
00200   }
00201 
00202   struct sockaddr_in addr;
00203   addr.sin_family = AF_INET;
00204   addr.sin_port = htons(_port);
00205   addr.sin_addr.s_addr = connectaddr;
00206 
00207   if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
00208     int savedErrno = errno;
00209     close();
00210     throw Exception("Could not connect() in ClientSocketDescriptor ctor", savedErrno);
00211   }
00212 
00213   // We're connected. Save the remote address away.
00214   socklen_t addrlen = sizeof(addr);
00215   if (getsockname(sock, (struct sockaddr *) &addr, &addrlen) != -1) {
00216     setLocalAddr(getNameByAddr((char *) &addr.sin_addr.s_addr), ntohs(addr.sin_port));
00217   }
00218 }
00219 
00220 ///////////////////////////////////////////////////////////////////////////
00221 
00222 ServerSocketDescriptor::ServerSocketDescriptor(ref<FileDescriptorManager> const &_fdMgr,
00223                                                int _port = 0, string const &_hostname = "")
00224   : SocketDescriptor(_fdMgr, socket(PF_INET, SOCK_STREAM, 0))
00225 {
00226   if (!isOpen())
00227     throw Exception("Could not create socket in ServerSocketDescriptor ctor", errno);
00228 
00229   int fd = getFd();
00230 
00231   {
00232     int i = 1;  // 1 == turn on the option
00233     // We don't care if this fails.
00234 #ifdef __WIN32__
00235     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof(i));
00236 #else
00237     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
00238 #endif
00239   }
00240 
00241   unsigned int bindaddr;                // in network order
00242   unsigned int localaddr;
00243 
00244   if (_hostname != "") {
00245     // We have a name to bind to.
00246     // We should use getsockname() to find our local address.
00247     if (!getAddrByName(_hostname, bindaddr)) {
00248       close();
00249       throw Exception("Host " + _hostname + " not found in ServerSocketDescriptor ctor");
00250     }
00251     localaddr = bindaddr;
00252   } else {
00253     // We have no name to bind to - use INADDR_ANY
00254     // We should use gethostname() to find our local address.
00255     char buf[256];
00256     if (gethostname(buf, 256) < 0) {
00257       close();
00258       throw Exception("Could not determine local hostname using gethostname(2)");
00259     }
00260 
00261     bindaddr = INADDR_ANY;
00262     if (!getAddrByName(buf, localaddr)) {
00263       localaddr = INADDR_ANY;
00264     }
00265   }
00266 
00267   struct sockaddr_in sa;
00268   sa.sin_family = AF_INET;
00269   sa.sin_port = htons(_port);
00270   sa.sin_addr.s_addr = bindaddr;
00271 
00272   if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
00273     char nbuf[40];
00274     sprintf(nbuf, "%d", _port);
00275     close();
00276     throw Exception("Could not bind to " + _hostname + ":" + nbuf);
00277   }
00278 
00279   if (listen(fd, 50) < 0) {
00280     close();
00281     throw Exception("Could not listen on socket");
00282   }
00283 
00284   // Use getsockname() here to get the port number. We ignore the
00285   // address returned - it's already calculated as localaddr above.
00286   socklen_t socknamelen = sizeof(sa);
00287 
00288   if (getsockname(fd, (struct sockaddr *) &sa, &socknamelen) < 0) {
00289     close();
00290     throw Exception("Could not determine local ServerSocket name");
00291   }
00292 
00293   {
00294     char addrbuf[100];
00295     sprintf(addrbuf, "%u.%u.%u.%u:%d",
00296             ((unsigned char *) &localaddr)[0],
00297             ((unsigned char *) &localaddr)[1],
00298             ((unsigned char *) &localaddr)[2],
00299             ((unsigned char *) &localaddr)[3],
00300             ntohs(sa.sin_port));
00301 
00302     canonicalAddress = addrbuf;
00303     setLocalAddr(getNameByAddr((char *) &localaddr), ntohs(sa.sin_port));
00304   }
00305 }
00306 
00307 ref<SocketDescriptor> ServerSocketDescriptor::accept() {
00308   struct sockaddr_in sa;
00309   socklen_t salen = sizeof(sa);
00310 
00311   int newfd = ::accept(getFd(), (struct sockaddr *) &sa, &salen);
00312   if (newfd == -1) {
00313     throw Exception("Could not accept() on ServerSocket", errno);
00314   }
00315 
00316   ref<SocketDescriptor> sock = new SocketDescriptor(getFdMgr(), newfd);
00317   sock->setRemoteAddr(getNameByAddr((char *) &sa.sin_addr.s_addr), ntohs(sa.sin_port));
00318 
00319   salen = sizeof(sa);
00320   if (getsockname(sock->getFd(), (struct sockaddr *) &sa, &salen) == -1) {
00321     throw Exception("Could not determine local ServerSocket name");
00322   }
00323 
00324   sock->setLocalAddr(getNameByAddr((char *) &sa.sin_addr.s_addr), ntohs(sa.sin_port));
00325 
00326   return sock;
00327 }

Generated at Wed Aug 15 01:05:16 2001 for mps-cpp by doxygen1.2.6 written by Dimitri van Heesch, © 1997-2001