Back to index

lightning-sunbird  0.9+nobinonly
nsSocket.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code, released
00016  * March 31, 1998.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Samir Gehani <sgehani@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include <stdio.h>
00041 #include <stdlib.h>
00042 #include <string.h>
00043 #include <errno.h>
00044 #include <ctype.h>
00045 #include <time.h>
00046 #ifdef _AIX
00047 #include <strings.h>
00048 #endif
00049 
00050 /* Platform-specific headers for socket functionality */
00051 #if defined(__unix) || defined(__unix__) || defined(macintosh) || \
00052     defined(_AIX) || defined(__OS2__)
00053 #include <unistd.h>
00054 #include <netdb.h>
00055 #include <sys/socket.h>
00056 #include <arpa/inet.h>
00057 #endif
00058 
00059 #if defined(_WINDOWS)
00060 #define read(_socket, _buf, _len) \
00061         recv(_socket, (char *) _buf, _len, 0);
00062 #define write(_socket, _buf, _len) \
00063         send(_socket, (char *) _buf, _len, 0);
00064 #include <winsock2.h>
00065 #endif
00066 
00067 #if defined(__OS2__)
00068 #define read(_socket, _buf, _len) \
00069         recv(_socket, (char *) _buf, _len, 0);
00070 #define write(_socket, _buf, _len) \
00071         send(_socket, (char *) _buf, _len, 0);
00072 #define close(_socket) \
00073         soclose(_socket);
00074 #define select(_socket, _readfd, _writefd, _exceptfd, _timeout) \
00075         bsdselect(_socket, _readfd, _writefd, _exceptfd, _timeout);
00076 #endif
00077 
00078 #include "nsSocket.h"
00079 
00080 #define MAXSOCKADDR 128
00081 
00082 #if (defined(SOLARIS) && !defined(_SOCKLEN_T)) || defined(_WINDOWS) || defined(IRIX) || defined(__OS2__)
00083 #define socklen_t int
00084 #endif
00085 
00086 #ifndef SHUT_RD
00087 #define SHUT_RD 0
00088 #endif
00089 #ifndef SHUT_WR
00090 #define SHUT_WR 1
00091 #endif
00092 #ifndef SHUT_RDWR
00093 #define SHUT_RDWR 2
00094 #endif
00095 
00096 const int kUsecsPerSec = 1000000;
00097 const int kRecvTimeoutThresholdUsecs = 30 * kUsecsPerSec;
00098 const int kTimeoutThresholdUsecs = 120 * kUsecsPerSec;
00099 const int kTimeoutSelectUsecs = 100000;
00100 const int kKilobyte = 1024;
00101 const int kUsecsPerKBFactor = (kUsecsPerSec/kKilobyte);
00102 const int kReadBufSize = 1024;
00103 
00104 #ifdef _WINDOWS
00105 static int sbWinSockInited = FALSE;
00106 #endif
00107 
00108 nsSocket::nsSocket(char *aHost, int aPort, int (*aEventPumpCB)(void)) :
00109     mEventPumpCB( aEventPumpCB ),
00110     mHost(aHost),
00111     mPort(aPort),
00112     mFd(-1),
00113     mListenFd(-1)
00114 {
00115 }
00116 
00117 nsSocket::nsSocket(char *aHost, int aPort) :
00118     mEventPumpCB( NULL ),
00119     mHost(aHost),
00120     mPort(aPort),
00121     mFd(-1),
00122     mListenFd(-1)
00123 {
00124 }
00125 
00126 nsSocket::~nsSocket()
00127 {
00128     // don't release mHost cause we don't own it
00129 }
00130 
00131 int
00132 nsSocket::Open()
00133 {
00134 #ifdef _WINDOWS
00135     /* funky windows initialization of winsock */
00136 
00137     int err;
00138     WSADATA wsaData;
00139     WORD wVersionRequested;
00140     
00141     /* We don't care which version we get because we're not
00142      * doing any specific to a particular winsock version. */
00143     /* Request for version 2.2 */
00144     wVersionRequested = MAKEWORD(2, 2);
00145     err = WSAStartup(wVersionRequested, &wsaData);
00146     if (err == WSAVERNOTSUPPORTED)
00147     {
00148         /* Request for version 1.1 */
00149         wVersionRequested = MAKEWORD(1, 1);
00150         err = WSAStartup(wVersionRequested, &wsaData);
00151         if (err == WSAVERNOTSUPPORTED)
00152         {
00153             /* Request for version 1.0 */
00154             wVersionRequested = MAKEWORD(0, 1);
00155             err = WSAStartup(wVersionRequested, &wsaData);
00156             if (err == WSAVERNOTSUPPORTED)
00157             {
00158                 /* Request for version 0.4 */
00159                 wVersionRequested = MAKEWORD(4, 0);
00160                 err = WSAStartup(wVersionRequested, &wsaData);
00161             }
00162         }
00163     }
00164 
00165     if (err != 0)
00166     {
00167         return E_WINSOCK;
00168     }
00169 #endif
00170 
00171     int rv = OK;
00172     struct sockaddr_in servaddr;
00173     struct hostent *hptr = NULL;
00174 
00175     mFd = socket(AF_INET, SOCK_STREAM, 0);
00176 #ifdef _WINDOWS
00177        if (mFd == INVALID_SOCKET) {
00178                      printf("Last error: %d\n", WSAGetLastError());
00179     }
00180     if ( mFd != INVALID_SOCKET ) {
00181 #else
00182     if ( mFd != -1 ) {
00183 #endif
00184 
00185         int windowSize = 32768;   // we could tune this, but for now...
00186         socklen_t newTCPWin, len;
00187        
00188         len = sizeof( newTCPWin );
00189  
00190         setsockopt( mFd, SOL_SOCKET, SO_RCVBUF, (char*) &windowSize, sizeof( windowSize ));
00191 #ifdef DEBUG
00192         getsockopt( mFd, SOL_SOCKET, SO_RCVBUF, (char*) &newTCPWin, &len );
00193 #endif
00194  
00195         setsockopt( mFd, SOL_SOCKET, SO_SNDBUF, (char*) &windowSize, sizeof( windowSize ));
00196 #ifdef DEBUG
00197         getsockopt( mFd, SOL_SOCKET, SO_RCVBUF, (char*) &newTCPWin, &len );
00198 #endif
00199 
00200         memset(&servaddr, 0, sizeof(servaddr));
00201         servaddr.sin_family = AF_INET;
00202         servaddr.sin_port = htons(mPort);
00203 
00204         if ((hptr = gethostbyname(mHost)) == NULL )
00205         {
00206             if (IsIPAddress(mHost) == OK)
00207             {
00208                 unsigned long netAddr;
00209 
00210                 netAddr = inet_addr(mHost);
00211                 if ((hptr = gethostbyaddr((char *)&netAddr, sizeof(unsigned long), AF_INET)) == NULL )
00212                     return E_INVALID_HOST;
00213             }
00214             else
00215             {
00216                 return E_INVALID_HOST;
00217             }
00218         }
00219 
00220         memcpy(&servaddr.sin_addr, (struct in_addr **) hptr->h_addr_list[0], sizeof(struct in_addr));
00221 
00222         rv = connect(mFd, (struct sockaddr *) &servaddr, sizeof(servaddr));
00223         if (rv < 0)
00224         {
00225 #if defined(DEBUG) && (defined(__unix) || defined(__unix__))
00226             printf("ETIMEDOUT: %d\n", ETIMEDOUT);
00227             printf("ECONNREFUSED: %d\n", ECONNREFUSED);
00228             printf("EHOSTUNREACH: %d\n", EHOSTUNREACH);
00229             printf("ENETUNREACH: %d\n", ENETUNREACH);
00230 
00231             printf("connect error: %d\n", errno);
00232 #endif /* DEBUG && (__unix || __unix__) */
00233             mFd = -1;
00234             rv = E_SOCK_OPEN;
00235         }
00236         else
00237             rv = OK;
00238     } else
00239         rv = E_SOCK_OPEN;
00240 
00241     return rv;
00242 }
00243 
00244 int
00245 nsSocket::SrvOpen()
00246 {
00247     int rv = OK;
00248     struct sockaddr_in servaddr;
00249 
00250     /* init data socket making it listen */
00251     mListenFd = socket(AF_INET, SOCK_STREAM, 0);
00252 
00253     memset(&servaddr, 0, sizeof(servaddr));
00254     servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* wildcard */
00255     servaddr.sin_port = 0; /* let kernel bind an ephemeral port */
00256     
00257     if ((bind(mListenFd, (struct sockaddr *) &servaddr, sizeof(servaddr))) != 0)
00258         return E_BIND;
00259 
00260     if ((listen(mListenFd, SOMAXCONN)) != 0)
00261         return E_LISTEN;
00262     return rv;
00263 }
00264 
00265 int 
00266 nsSocket::SrvAccept()
00267 {
00268     int rv = OK;
00269     struct sockaddr cliaddr;
00270     socklen_t clilen;
00271 
00272     if (mListenFd < 0)
00273         return E_PARAM;
00274 
00275     clilen = sizeof(cliaddr);
00276     mFd = accept(mListenFd, (struct sockaddr *) &cliaddr, &clilen);
00277     if (mFd < 0)
00278         rv = E_ACCEPT;
00279 
00280     return rv;
00281 }
00282 
00283 int 
00284 nsSocket::Send(unsigned char *aBuf, int *aBufSize)
00285 {
00286     int rv = OK;
00287     struct timeval seltime;
00288     int timeout = 0;
00289     fd_set selset;
00290 
00291     if (!aBuf || (aBufSize && (*aBufSize <= 0)) || mFd < 0)
00292         return E_PARAM;
00293 
00294     while (timeout < kTimeoutThresholdUsecs)
00295     {
00296         FD_ZERO(&selset);
00297         FD_SET(mFd, &selset);
00298         seltime.tv_sec = 0;
00299         seltime.tv_usec = kTimeoutSelectUsecs;
00300 
00301         if ( mEventPumpCB != NULL )
00302             if (mEventPumpCB() == E_USER_CANCEL)
00303                 return E_USER_CANCEL;
00304 
00305         rv = select(mFd+1, NULL, &selset, NULL, &seltime);
00306         switch (rv)
00307         {
00308             case -1:            /* error occured! */
00309                 return errno;
00310             case 0:             /* timeout; retry */
00311                 timeout += kTimeoutSelectUsecs;
00312                 continue;
00313             default:            /* ready to write */
00314                 timeout = 0;    /* reset the time out counter */
00315                 break;
00316         }
00317 
00318         if (!FD_ISSET(mFd, &selset))
00319         {
00320             timeout += kTimeoutSelectUsecs;
00321             continue;           /* not ready to write; retry */
00322         }
00323         else
00324             break;
00325     }
00326     if (rv == 0)
00327         return E_TIMEOUT;
00328 
00329     rv = write(mFd, aBuf, *aBufSize);
00330     if (rv <= 0)
00331         rv = E_WRITE;
00332     else
00333     {
00334         *aBufSize = rv;
00335         rv = OK;
00336     }
00337 
00338     return rv;
00339 }
00340 
00341 int
00342 nsSocket::Recv(unsigned char *aBuf, int *aBufSize)
00343 {
00344   return(Recv(aBuf, aBufSize, kRecvTimeoutThresholdUsecs));
00345 }
00346 
00347 int
00348 nsSocket::Recv(unsigned char *aBuf, int *aBufSize, int aTimeoutThresholdUsecs)
00349 {
00350     int  rv = OK;
00351     unsigned char lbuf[kReadBufSize]; /* function local buffer */
00352     int bytesrd = 0;
00353     struct timeval seltime;
00354     fd_set selset;
00355     int bufsize;
00356     int timeout;
00357 
00358     if (!aBuf || (aBufSize && (*aBufSize <= 0)) || mFd < 0)
00359         return E_PARAM;
00360     memset(aBuf, 0, *aBufSize);
00361 
00362     timeout = 0;
00363     while (timeout < aTimeoutThresholdUsecs)
00364     {
00365         /* return if we anticipate overflowing caller's buffer */
00366         if (bytesrd >= *aBufSize)
00367             return E_READ_MORE;
00368 
00369         memset(&lbuf, 0, kReadBufSize);
00370 
00371         FD_ZERO(&selset);
00372         FD_SET(mFd, &selset);
00373         seltime.tv_sec = 0;
00374         seltime.tv_usec = kTimeoutSelectUsecs;
00375 
00376         if ( mEventPumpCB != NULL )
00377             if (mEventPumpCB() == E_USER_CANCEL)
00378                 return E_USER_CANCEL;
00379 
00380         rv = select(mFd+1, &selset, NULL, NULL, &seltime);
00381         switch (rv)
00382         {
00383             case -1:            /* error occured! */
00384                 return errno;
00385             case 0:             /* timeout; retry */
00386                 timeout += kTimeoutSelectUsecs;
00387                 continue;
00388             default:            /* ready to read */
00389                 timeout = 0;    /* reset the time out counter */
00390                 break;
00391         }
00392 
00393         if (!FD_ISSET(mFd, &selset))
00394         {
00395             timeout += kTimeoutSelectUsecs;
00396             continue;           /* not ready to read; retry */
00397         }
00398 
00399         bufsize = *aBufSize - bytesrd;
00400         rv = read(mFd, lbuf, bufsize);
00401         if (rv == 0) /* EOF encountered */
00402         {
00403             rv = E_EOF_FOUND;
00404             break;
00405         }
00406         if (rv < 0)
00407         {
00408             rv = E_READ;
00409             break;
00410         }
00411 
00412         if (*aBufSize >= bytesrd + rv)
00413         {
00414             memcpy(aBuf + bytesrd, lbuf, rv);
00415             bytesrd += rv;
00416             if (rv <= bufsize)
00417             {
00418                 FD_ZERO(&selset);
00419                 FD_SET(mFd, &selset);
00420                 seltime.tv_sec = 0;
00421                 seltime.tv_usec = kTimeoutSelectUsecs;
00422 
00423                 /* check if we still need to read from this socket */
00424                 rv = select(mFd+1, &selset, NULL, NULL, &seltime);
00425                 if (rv == 1)
00426                     rv = E_READ_MORE;
00427                 else
00428                     rv = OK;
00429                 break;
00430             }
00431         }
00432         else
00433         {
00434             rv = E_SMALL_BUF;
00435             break;
00436         }
00437     }
00438     if (timeout >= aTimeoutThresholdUsecs)
00439         return E_TIMEOUT;
00440 
00441     *aBufSize = bytesrd;
00442     return rv;
00443 }
00444 
00445 int
00446 nsSocket::Close()
00447 {
00448     int rv = OK, rv1 = OK, rv2 = OK;
00449 
00450 /* funky windows shutdown of winsock */
00451 #ifdef _WINDOWS
00452     closesocket(mFd);
00453     if (mListenFd > 0)
00454         closesocket(mListenFd);
00455     
00456     if (rv1 != 0 || rv2 != 0)
00457         rv = E_SOCK_CLOSE; 
00458 
00459     int wsaErr = WSACleanup();
00460     if (wsaErr != 0)
00461         rv = wsaErr;
00462 #else /* unix or mac */
00463     rv1 = close(mFd);
00464     if (mListenFd > 0)
00465         rv2 = close(mListenFd);
00466     if (rv1 != 0 || rv2 != 0)
00467         rv = E_SOCK_CLOSE; 
00468 #endif
00469 
00470     return rv;
00471 }
00472 
00473 int
00474 nsSocket::GetHostPortString(char **aHostPort)
00475 {
00476     int rv = OK;
00477     socklen_t salen;
00478     struct sockaddr_in servaddr;
00479     int hpsLen; // host-port string length
00480 
00481     if (!aHostPort)
00482         return E_PARAM;
00483 
00484     salen = MAXSOCKADDR;
00485     if ((getsockname(mListenFd, (struct sockaddr *) &servaddr, &salen)) < 0)
00486     {
00487         *aHostPort = NULL;
00488         return E_GETSOCKNAME;
00489     }
00490 
00491     hpsLen = strlen("AA1,AA2,AA3,AA4,PP1,PP2");
00492     *aHostPort = (char *) malloc(hpsLen);
00493     if (!*aHostPort)
00494        return E_MEM;
00495  
00496     memset(*aHostPort, 0, hpsLen);
00497     sprintf(*aHostPort, "%d,%d,%d,%d,%d,%d",
00498       (int)((char*)&servaddr.sin_addr)[0] & 0xFF,
00499       (int)((char*)&servaddr.sin_addr)[1] & 0xFF,
00500       (int)((char*)&servaddr.sin_addr)[2] & 0xFF,
00501       (int)((char*)&servaddr.sin_addr)[3] & 0xFF,
00502       (int)((char*)&servaddr.sin_port)[0] & 0xFF,
00503       (int)((char*)&servaddr.sin_port)[1] & 0xFF);
00504 
00505     return rv;    
00506 }
00507 
00508 int
00509 nsSocket::IsIPAddress(char *aAddress)
00510 {
00511     int addr[4];
00512     int numDots = 0;
00513     
00514     for (unsigned int i=0; i < strlen(aAddress); ++i)
00515     {
00516         if (isspace(aAddress[i]))
00517             return E_INVALID_ADDR;
00518         if (aAddress[i] == '.')
00519         {
00520             ++numDots;
00521             if (numDots > 3)
00522                 return E_INVALID_ADDR;
00523         }
00524         else if (!isdigit(aAddress[i]))
00525             return E_INVALID_ADDR;
00526     }
00527 
00528     if (sscanf(aAddress, "%d.%d.%d.%d", 
00529         &addr[0], &addr[1], &addr[2], &addr[3]) != 4)
00530         return E_INVALID_ADDR;
00531 
00532     if ((addr[0] > 255) || 
00533         (addr[1] > 255) || 
00534         (addr[2] > 255) || 
00535         (addr[3] > 255))
00536         return E_INVALID_ADDR;
00537 
00538     return OK;
00539 }
00540 
00541 float
00542 nsSocket::CalcRate(struct timeval *aPre, struct timeval *aPost, int aBytes)
00543 {
00544     float diffUsecs, rate;
00545 
00546     /* param check */
00547     if (!aPre || !aPost || aBytes <= 0)
00548         return 0;
00549     
00550     diffUsecs = (float)(aPost->tv_sec - aPre->tv_sec) * kUsecsPerSec;
00551     diffUsecs += (float)aPost->tv_usec - (float)aPre->tv_usec;
00552     rate = ((float)aBytes)/((float)diffUsecs) * kUsecsPerKBFactor;
00553 
00554     return rate;
00555 }
00556 
00557 #ifdef TEST_NSSOCKET
00558 
00559 void 
00560 my_nprintf(char *buf, int len)
00561 {
00562     printf("buf size = %d\n", len);
00563     for (int i = 0; i < len; ++i)
00564     {
00565         printf("%c", *(buf+i));
00566     }
00567     printf("\n");
00568 }
00569 
00570 const int kTestBufSize = 1024;
00571 
00572 int
00573 main(int argc, char **argv)
00574 {
00575     DUMP(("*** %s: A self-test for nsSocket.\n", argv[0]));
00576     
00577     if (argc < 4)
00578     {
00579         fprintf(stderr, "usage: %s <host> <port> <http_url>\n", 
00580                 argv[0]);
00581         exit(1);
00582     }
00583 
00584     int rv = nsSocket::OK;
00585     nsSocket *sock = new nsSocket(argv[1], atoi(argv[2]));
00586     char buf[kTestBufSize];
00587     int bufSize;
00588     memset(buf, 0, kTestBufSize);
00589     
00590     // open socket
00591     rv = sock->Open();
00592     DUMP(("nsSocket::Open returned: %d\n", rv));
00593 
00594     // prepare http request str
00595     sprintf(buf, "GET %s HTTP/1.0\r\n\r\n", argv[3]);
00596     bufSize = strlen(buf) + 1; // add 1 for NULL termination
00597 
00598     // make request
00599     rv = sock->Send((unsigned char *)buf, &bufSize);
00600     DUMP(("nsSocket::Send returned: %d\t and sent: %d bytes\n", rv, bufSize));
00601     
00602     // get response
00603     do {
00604         // prepare response buf
00605         memset(buf, 0, kTestBufSize);
00606         bufSize = kTestBufSize;
00607 
00608         rv = sock->Recv((unsigned char *)buf, &bufSize);
00609 
00610         DUMP(("nsSocket::Recv returned: %d\t and recd: %d bytes\n", 
00611                rv, bufSize));
00612         // DUMP(("%s\n", buf));
00613         my_nprintf(buf, bufSize);
00614     } while (rv == nsSocket::E_READ_MORE);
00615 
00616     // close socket
00617     rv = sock->Close();
00618     DUMP(("nsSocket::Close returned: %d\n", rv));
00619 
00620     return 0;
00621 }
00622 #endif /* TEST_NSSOCKET */