gerv%gerv.net f4294ea0f8 Bug 236613: change to MPL/LGPL/GPL tri-license.
git-svn-id: svn://10.0.0.236/trunk@155026 18797224-902f-48f8-a5cc-f745e15eee43
2004-04-17 14:37:35 +00:00

623 lines
16 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Samir Gehani <sgehani@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#ifdef _AIX
#include <strings.h>
#endif
/* Platform-specific headers for socket functionality */
#if defined(__unix) || defined(__unix__) || defined(macintosh) || \
defined(_AIX) || defined(__OS2__)
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#endif
#if defined(_WINDOWS)
#define read(_socket, _buf, _len) \
recv(_socket, (char *) _buf, _len, 0);
#define write(_socket, _buf, _len) \
send(_socket, (char *) _buf, _len, 0);
#include <winsock2.h>
#endif
#if defined(__OS2__)
#define read(_socket, _buf, _len) \
recv(_socket, (char *) _buf, _len, 0);
#define write(_socket, _buf, _len) \
send(_socket, (char *) _buf, _len, 0);
#define close(_socket) \
soclose(_socket);
#define select(_socket, _readfd, _writefd, _exceptfd, _timeout) \
bsdselect(_socket, _readfd, _writefd, _exceptfd, _timeout);
#endif
#include "nsSocket.h"
#define MAXSOCKADDR 128
#if (defined(SOLARIS) && !defined(_SOCKLEN_T)) || defined(_WINDOWS) || defined(IRIX) || defined(__OS2__)
#define socklen_t int
#endif
#ifndef SHUT_RD
#define SHUT_RD 0
#endif
#ifndef SHUT_WR
#define SHUT_WR 1
#endif
#ifndef SHUT_RDWR
#define SHUT_RDWR 2
#endif
const int kUsecsPerSec = 1000000;
const int kRecvTimeoutThresholdUsecs = 30 * kUsecsPerSec;
const int kTimeoutThresholdUsecs = 120 * kUsecsPerSec;
const int kTimeoutSelectUsecs = 100000;
const int kKilobyte = 1024;
const int kUsecsPerKBFactor = (kUsecsPerSec/kKilobyte);
const int kReadBufSize = 1024;
#ifdef _WINDOWS
static int sbWinSockInited = FALSE;
#endif
nsSocket::nsSocket(char *aHost, int aPort, int (*aEventPumpCB)(void)) :
mEventPumpCB( aEventPumpCB ),
mHost(aHost),
mPort(aPort),
mFd(-1),
mListenFd(-1)
{
}
nsSocket::nsSocket(char *aHost, int aPort) :
mEventPumpCB( NULL ),
mHost(aHost),
mPort(aPort),
mFd(-1),
mListenFd(-1)
{
}
nsSocket::~nsSocket()
{
// don't release mHost cause we don't own it
}
int
nsSocket::Open()
{
#ifdef _WINDOWS
/* funky windows initialization of winsock */
int err;
WSADATA wsaData;
WORD wVersionRequested;
/* We don't care which version we get because we're not
* doing any specific to a particular winsock version. */
/* Request for version 2.2 */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err == WSAVERNOTSUPPORTED)
{
/* Request for version 1.1 */
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup(wVersionRequested, &wsaData);
if (err == WSAVERNOTSUPPORTED)
{
/* Request for version 1.0 */
wVersionRequested = MAKEWORD(0, 1);
err = WSAStartup(wVersionRequested, &wsaData);
if (err == WSAVERNOTSUPPORTED)
{
/* Request for version 0.4 */
wVersionRequested = MAKEWORD(4, 0);
err = WSAStartup(wVersionRequested, &wsaData);
}
}
}
if (err != 0)
{
return E_WINSOCK;
}
#endif
int rv = OK;
struct sockaddr_in servaddr;
struct hostent *hptr = NULL;
mFd = socket(AF_INET, SOCK_STREAM, 0);
#ifdef _WINDOWS
if (mFd == INVALID_SOCKET) {
printf("Last error: %d\n", WSAGetLastError());
}
if ( mFd != INVALID_SOCKET ) {
#else
if ( mFd != -1 ) {
#endif
int windowSize = 32768; // we could tune this, but for now...
socklen_t newTCPWin, len;
len = sizeof( newTCPWin );
setsockopt( mFd, SOL_SOCKET, SO_RCVBUF, (char*) &windowSize, sizeof( windowSize ));
#ifdef DEBUG
getsockopt( mFd, SOL_SOCKET, SO_RCVBUF, (char*) &newTCPWin, &len );
#endif
setsockopt( mFd, SOL_SOCKET, SO_SNDBUF, (char*) &windowSize, sizeof( windowSize ));
#ifdef DEBUG
getsockopt( mFd, SOL_SOCKET, SO_RCVBUF, (char*) &newTCPWin, &len );
#endif
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(mPort);
if ((hptr = gethostbyname(mHost)) == NULL )
{
if (IsIPAddress(mHost) == OK)
{
unsigned long netAddr;
netAddr = inet_addr(mHost);
if ((hptr = gethostbyaddr((char *)&netAddr, sizeof(unsigned long), AF_INET)) == NULL )
return E_INVALID_HOST;
}
else
{
return E_INVALID_HOST;
}
}
memcpy(&servaddr.sin_addr, (struct in_addr **) hptr->h_addr_list[0], sizeof(struct in_addr));
rv = connect(mFd, (struct sockaddr *) &servaddr, sizeof(servaddr));
if (rv < 0)
{
#if defined(DEBUG) && (defined(__unix) || defined(__unix__))
printf("ETIMEDOUT: %d\n", ETIMEDOUT);
printf("ECONNREFUSED: %d\n", ECONNREFUSED);
printf("EHOSTUNREACH: %d\n", EHOSTUNREACH);
printf("ENETUNREACH: %d\n", ENETUNREACH);
printf("connect error: %d\n", errno);
#endif /* DEBUG && (__unix || __unix__) */
mFd = -1;
rv = E_SOCK_OPEN;
}
else
rv = OK;
} else
rv = E_SOCK_OPEN;
return rv;
}
int
nsSocket::SrvOpen()
{
int rv = OK;
struct sockaddr_in servaddr;
/* init data socket making it listen */
mListenFd = socket(AF_INET, SOCK_STREAM, 0);
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* wildcard */
servaddr.sin_port = 0; /* let kernel bind an ephemeral port */
if ((bind(mListenFd, (struct sockaddr *) &servaddr, sizeof(servaddr))) != 0)
return E_BIND;
if ((listen(mListenFd, SOMAXCONN)) != 0)
return E_LISTEN;
return rv;
}
int
nsSocket::SrvAccept()
{
int rv = OK;
struct sockaddr cliaddr;
socklen_t clilen;
if (mListenFd < 0)
return E_PARAM;
clilen = sizeof(cliaddr);
mFd = accept(mListenFd, (struct sockaddr *) &cliaddr, &clilen);
if (mFd < 0)
rv = E_ACCEPT;
return rv;
}
int
nsSocket::Send(unsigned char *aBuf, int *aBufSize)
{
int rv = OK;
struct timeval seltime;
int timeout = 0;
fd_set selset;
if (!aBuf || (aBufSize && (*aBufSize <= 0)) || mFd < 0)
return E_PARAM;
while (timeout < kTimeoutThresholdUsecs)
{
FD_ZERO(&selset);
FD_SET(mFd, &selset);
seltime.tv_sec = 0;
seltime.tv_usec = kTimeoutSelectUsecs;
if ( mEventPumpCB != NULL )
if (mEventPumpCB() == E_USER_CANCEL)
return E_USER_CANCEL;
rv = select(mFd+1, NULL, &selset, NULL, &seltime);
switch (rv)
{
case -1: /* error occured! */
return errno;
case 0: /* timeout; retry */
timeout += kTimeoutSelectUsecs;
continue;
default: /* ready to write */
timeout = 0; /* reset the time out counter */
break;
}
if (!FD_ISSET(mFd, &selset))
{
timeout += kTimeoutSelectUsecs;
continue; /* not ready to write; retry */
}
else
break;
}
if (rv == 0)
return E_TIMEOUT;
rv = write(mFd, aBuf, *aBufSize);
if (rv <= 0)
rv = E_WRITE;
else
{
*aBufSize = rv;
rv = OK;
}
return rv;
}
int
nsSocket::Recv(unsigned char *aBuf, int *aBufSize)
{
return(Recv(aBuf, aBufSize, kRecvTimeoutThresholdUsecs));
}
int
nsSocket::Recv(unsigned char *aBuf, int *aBufSize, int aTimeoutThresholdUsecs)
{
int rv = OK;
unsigned char lbuf[kReadBufSize]; /* function local buffer */
int bytesrd = 0;
struct timeval seltime;
fd_set selset;
int bufsize;
int timeout;
if (!aBuf || (aBufSize && (*aBufSize <= 0)) || mFd < 0)
return E_PARAM;
memset(aBuf, 0, *aBufSize);
timeout = 0;
while (timeout < aTimeoutThresholdUsecs)
{
/* return if we anticipate overflowing caller's buffer */
if (bytesrd >= *aBufSize)
return E_READ_MORE;
memset(&lbuf, 0, kReadBufSize);
FD_ZERO(&selset);
FD_SET(mFd, &selset);
seltime.tv_sec = 0;
seltime.tv_usec = kTimeoutSelectUsecs;
if ( mEventPumpCB != NULL )
if (mEventPumpCB() == E_USER_CANCEL)
return E_USER_CANCEL;
rv = select(mFd+1, &selset, NULL, NULL, &seltime);
switch (rv)
{
case -1: /* error occured! */
return errno;
case 0: /* timeout; retry */
timeout += kTimeoutSelectUsecs;
continue;
default: /* ready to read */
timeout = 0; /* reset the time out counter */
break;
}
if (!FD_ISSET(mFd, &selset))
{
timeout += kTimeoutSelectUsecs;
continue; /* not ready to read; retry */
}
bufsize = *aBufSize - bytesrd;
rv = read(mFd, lbuf, bufsize);
if (rv == 0) /* EOF encountered */
{
rv = E_EOF_FOUND;
break;
}
if (rv < 0)
{
rv = E_READ;
break;
}
if (*aBufSize >= bytesrd + rv)
{
memcpy(aBuf + bytesrd, lbuf, rv);
bytesrd += rv;
if (rv <= bufsize)
{
FD_ZERO(&selset);
FD_SET(mFd, &selset);
seltime.tv_sec = 0;
seltime.tv_usec = kTimeoutSelectUsecs;
/* check if we still need to read from this socket */
rv = select(mFd+1, &selset, NULL, NULL, &seltime);
if (rv == 1)
rv = E_READ_MORE;
else
rv = OK;
break;
}
}
else
{
rv = E_SMALL_BUF;
break;
}
}
if (timeout >= aTimeoutThresholdUsecs)
return E_TIMEOUT;
*aBufSize = bytesrd;
return rv;
}
int
nsSocket::Close()
{
int rv = OK, rv1 = OK, rv2 = OK;
/* funky windows shutdown of winsock */
#ifdef _WINDOWS
closesocket(mFd);
if (mListenFd > 0)
closesocket(mListenFd);
if (rv1 != 0 || rv2 != 0)
rv = E_SOCK_CLOSE;
int wsaErr = WSACleanup();
if (wsaErr != 0)
rv = wsaErr;
#else /* unix or mac */
rv1 = close(mFd);
if (mListenFd > 0)
rv2 = close(mListenFd);
if (rv1 != 0 || rv2 != 0)
rv = E_SOCK_CLOSE;
#endif
return rv;
}
int
nsSocket::GetHostPortString(char **aHostPort)
{
int rv = OK;
socklen_t salen;
struct sockaddr_in servaddr;
int hpsLen; // host-port string length
if (!aHostPort)
return E_PARAM;
salen = MAXSOCKADDR;
if ((getsockname(mListenFd, (struct sockaddr *) &servaddr, &salen)) < 0)
{
*aHostPort = NULL;
return E_GETSOCKNAME;
}
hpsLen = strlen("AA1,AA2,AA3,AA4,PP1,PP2");
*aHostPort = (char *) malloc(hpsLen);
if (!*aHostPort)
return E_MEM;
memset(*aHostPort, 0, hpsLen);
sprintf(*aHostPort, "%d,%d,%d,%d,%d,%d",
(int)((char*)&servaddr.sin_addr)[0] & 0xFF,
(int)((char*)&servaddr.sin_addr)[1] & 0xFF,
(int)((char*)&servaddr.sin_addr)[2] & 0xFF,
(int)((char*)&servaddr.sin_addr)[3] & 0xFF,
(int)((char*)&servaddr.sin_port)[0] & 0xFF,
(int)((char*)&servaddr.sin_port)[1] & 0xFF);
return rv;
}
int
nsSocket::IsIPAddress(char *aAddress)
{
int addr[4];
int numDots = 0;
for (unsigned int i=0; i < strlen(aAddress); ++i)
{
if (isspace(aAddress[i]))
return E_INVALID_ADDR;
if (aAddress[i] == '.')
{
++numDots;
if (numDots > 3)
return E_INVALID_ADDR;
}
else if (!isdigit(aAddress[i]))
return E_INVALID_ADDR;
}
if (sscanf(aAddress, "%d.%d.%d.%d",
&addr[0], &addr[1], &addr[2], &addr[3]) != 4)
return E_INVALID_ADDR;
if ((addr[0] > 255) ||
(addr[1] > 255) ||
(addr[2] > 255) ||
(addr[3] > 255))
return E_INVALID_ADDR;
return OK;
}
float
nsSocket::CalcRate(struct timeval *aPre, struct timeval *aPost, int aBytes)
{
float diffUsecs, rate;
/* param check */
if (!aPre || !aPost || aBytes <= 0)
return 0;
diffUsecs = (float)(aPost->tv_sec - aPre->tv_sec) * kUsecsPerSec;
diffUsecs += (float)aPost->tv_usec - (float)aPre->tv_usec;
rate = ((float)aBytes)/((float)diffUsecs) * kUsecsPerKBFactor;
return rate;
}
#ifdef TEST_NSSOCKET
void
my_nprintf(char *buf, int len)
{
printf("buf size = %d\n", len);
for (int i = 0; i < len; ++i)
{
printf("%c", *(buf+i));
}
printf("\n");
}
const int kTestBufSize = 1024;
int
main(int argc, char **argv)
{
DUMP(("*** %s: A self-test for nsSocket.\n", argv[0]));
if (argc < 4)
{
fprintf(stderr, "usage: %s <host> <port> <http_url>\n",
argv[0]);
exit(1);
}
int rv = nsSocket::OK;
nsSocket *sock = new nsSocket(argv[1], atoi(argv[2]));
char buf[kTestBufSize];
int bufSize;
memset(buf, 0, kTestBufSize);
// open socket
rv = sock->Open();
DUMP(("nsSocket::Open returned: %d\n", rv));
// prepare http request str
sprintf(buf, "GET %s HTTP/1.0\r\n\r\n", argv[3]);
bufSize = strlen(buf) + 1; // add 1 for NULL termination
// make request
rv = sock->Send((unsigned char *)buf, &bufSize);
DUMP(("nsSocket::Send returned: %d\t and sent: %d bytes\n", rv, bufSize));
// get response
do {
// prepare response buf
memset(buf, 0, kTestBufSize);
bufSize = kTestBufSize;
rv = sock->Recv((unsigned char *)buf, &bufSize);
DUMP(("nsSocket::Recv returned: %d\t and recd: %d bytes\n",
rv, bufSize));
// DUMP(("%s\n", buf));
my_nprintf(buf, bufSize);
} while (rv == nsSocket::E_READ_MORE);
// close socket
rv = sock->Close();
DUMP(("nsSocket::Close returned: %d\n", rv));
return 0;
}
#endif /* TEST_NSSOCKET */