diff options
author | cvs2hg <devnull@localhost> | 2001-12-10 22:27:33 +0000 |
---|---|---|
committer | cvs2hg <devnull@localhost> | 2001-12-10 22:27:33 +0000 |
commit | dc6bc174e993ea4b6316aabf3d2974c86585d6ae (patch) | |
tree | 09c6b1d828292dc907b067746aef389f56f3a956 | |
parent | a47df1cefbbbd5c3c9dd2acd10d62cfd112dada7 (diff) | |
download | nspr-hg-MOZILLA_0_9_7_BRANCH.tar.gz |
fixup commit for branch 'MOZILLA_0_9_7_BRANCH'MOZILLA_0_9_7_RELEASEMOZILLA_0_9_7_BASEMOZILLA_0_9_7_20011221MOZILLA_0_9_7_BRANCH
-rw-r--r-- | pr/include/prlog.h | 253 | ||||
-rw-r--r-- | pr/src/io/prlog.c | 504 | ||||
-rw-r--r-- | pr/src/io/prsocket.c | 1805 |
3 files changed, 2562 insertions, 0 deletions
diff --git a/pr/include/prlog.h b/pr/include/prlog.h new file mode 100644 index 00000000..b7a7b5a0 --- /dev/null +++ b/pr/include/prlog.h @@ -0,0 +1,253 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * 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 the Netscape Portable Runtime (NSPR). + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef prlog_h___ +#define prlog_h___ + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/* +** prlog.h -- Declare interfaces to NSPR's Logging service +** +** NSPR provides a logging service that is used by NSPR itself and is +** available to client programs. +** +** To use the service from a client program, you should create a +** PRLogModuleInfo structure by calling PR_NewLogModule(). After +** creating the LogModule, you can write to the log using the PR_LOG() +** macro. +** +** Initialization of the log service is handled by NSPR initialization. +** +** At execution time, you must enable the log service. To enable the +** log service, set the environment variable: NSPR_LOG_MODULES +** variable. +** +** NSPR_LOG_MODULES variable has the form: +** +** <moduleName>:<value>[, <moduleName>:<value>]* +** +** Where: +** <moduleName> is the name passed to PR_NewLogModule(). +** <value> is a numeric constant, e.g. 5. This value is the maximum +** value of a log event, enumerated by PRLogModuleLevel, that you want +** written to the log. +** +** For example: to record all events of greater value than or equal to +** PR_LOG_ERROR for a LogModule names "gizmo", say: +** +** set NSPR_LOG_MODULES=gizmo:2 +** +** Note that you must specify the numeric value of PR_LOG_ERROR. +** +** Special LogModule names are provided for controlling NSPR's log +** service at execution time. These controls should be set in the +** NSPR_LOG_MODULES environment variable at execution time to affect +** NSPR's log service for your application. +** +** The special LogModule "all" enables all LogModules. To enable all +** LogModule calls to PR_LOG(), say: +** +** set NSPR_LOG_MODULES=all:5 +** +** The special LogModule name "sync" tells the NSPR log service to do +** unbuffered logging. +** +** The special LogModule name "bufsize:<size>" tells NSPR to set the +** log buffer to <size>. +** +** The environment variable NSPR_LOG_FILE specifies the log file to use +** unless the default of "stderr" is acceptable. For MS Windows +** systems, NSPR_LOG_FILE can be set to a special value: "WinDebug" +** (case sensitive). This value causes PR_LOG() output to be written +** using the Windows API OutputDebugString(). OutputDebugString() +** writes to the debugger window; some people find this helpful. +** +** +** To put log messages in your programs, use the PR_LOG macro: +** +** PR_LOG(<module>, <level>, (<printfString>, <args>*)); +** +** Where <module> is the address of a PRLogModuleInfo structure, and +** <level> is one of the levels defined by the enumeration: +** PRLogModuleLevel. <args> is a printf() style of argument list. That +** is: (fmtstring, ...). +** +** Example: +** +** main() { +** PRIntn one = 1; +** PRLogModuleInfo * myLm = PR_NewLogModule("gizmo"); +** PR_LOG( myLm, PR_LOG_ALWAYS, ("Log this! %d\n", one)); +** return; +** } +** +** Note the use of printf() style arguments as the third agrument(s) to +** PR_LOG(). +** +** After compiling and linking you application, set the environment: +** +** set NSPR_LOG_MODULES=gizmo:5 +** set NSPR_LOG_FILE=logfile.txt +** +** When you execute your application, the string "Log this! 1" will be +** written to the file "logfile.txt". +** +** Note to NSPR engineers: a number of PRLogModuleInfo structures are +** defined and initialized in prinit.c. See this module for ideas on +** what to log where. +** +*/ + +typedef enum PRLogModuleLevel { + PR_LOG_NONE = 0, /* nothing */ + PR_LOG_ALWAYS = 1, /* always printed */ + PR_LOG_ERROR = 2, /* error messages */ + PR_LOG_WARNING = 3, /* warning messages */ + PR_LOG_DEBUG = 4, /* debug messages */ + + PR_LOG_NOTICE = PR_LOG_DEBUG, /* notice messages */ + PR_LOG_WARN = PR_LOG_WARNING, /* warning messages */ + PR_LOG_MIN = PR_LOG_DEBUG, /* minimal debugging messages */ + PR_LOG_MAX = PR_LOG_DEBUG /* maximal debugging messages */ +} PRLogModuleLevel; + +/* +** One of these structures is created for each module that uses logging. +** "name" is the name of the module +** "level" is the debugging level selected for that module +*/ +typedef struct PRLogModuleInfo { + const char *name; + PRLogModuleLevel level; + struct PRLogModuleInfo *next; +} PRLogModuleInfo; + +/* +** Create a new log module. +*/ +NSPR_API(PRLogModuleInfo*) PR_NewLogModule(const char *name); + +/* +** Set the file to use for logging. Returns PR_FALSE if the file cannot +** be created +*/ +NSPR_API(PRBool) PR_SetLogFile(const char *name); + +/* +** Set the size of the logging buffer. If "buffer_size" is zero then the +** logging becomes "synchronous" (or unbuffered). +*/ +NSPR_API(void) PR_SetLogBuffering(PRIntn buffer_size); + +/* +** Print a string to the log. "fmt" is a PR_snprintf format type. All +** messages printed to the log are preceeded by the name of the thread +** and a time stamp. Also, the routine provides a missing newline if one +** is not provided. +*/ +NSPR_API(void) PR_LogPrint(const char *fmt, ...); + +/* +** Flush the log to its file. +*/ +NSPR_API(void) PR_LogFlush(void); + +/* +** Windoze 16 can't support a large static string space for all of the +** various debugging strings so logging is not enabled for it. +*/ +#if (defined(DEBUG) || defined(FORCE_PR_LOG)) && !defined(WIN16) +#define PR_LOGGING 1 + +#define PR_LOG_TEST(_module,_level) \ + ((_module)->level >= (_level)) + +/* +** Log something. +** "module" is the address of a PRLogModuleInfo structure +** "level" is the desired logging level +** "args" is a variable length list of arguments to print, in the following +** format: ("printf style format string", ...) +*/ +#define PR_LOG(_module,_level,_args) \ + PR_BEGIN_MACRO \ + if (PR_LOG_TEST(_module,_level)) { \ + PR_LogPrint _args; \ + } \ + PR_END_MACRO + +#else /* (defined(DEBUG) || defined(FORCE_PR_LOG)) && !defined(WIN16) */ + +#undef PR_LOGGING +#define PR_LOG_TEST(module,level) 0 +#define PR_LOG(module,level,args) + +#endif /* (defined(DEBUG) || defined(FORCE_PR_LOG)) && !defined(WIN16) */ + +#ifndef NO_NSPR_10_SUPPORT + +#ifdef PR_LOGGING +#define PR_LOG_BEGIN PR_LOG +#define PR_LOG_END PR_LOG +#define PR_LOG_DEFINE PR_NewLogModule +#else +#define PR_LOG_BEGIN(module,level,args) +#define PR_LOG_END(module,level,args) +#define PR_LOG_DEFINE(_name) NULL +#endif /* PR_LOGGING */ + +#endif /* NO_NSPR_10_SUPPORT */ + +#if defined(DEBUG) + +NSPR_API(void) PR_Assert(const char *s, const char *file, PRIntn ln); +#define PR_ASSERT(_expr) \ + ((_expr)?((void)0):PR_Assert(# _expr,__FILE__,__LINE__)) + +#define PR_NOT_REACHED(_reasonStr) \ + PR_Assert(_reasonStr,__FILE__,__LINE__) + +#else + +#define PR_ASSERT(expr) ((void) 0) +#define PR_NOT_REACHED(reasonStr) + +#endif /* defined(DEBUG) */ + +PR_END_EXTERN_C + +#endif /* prlog_h___ */ diff --git a/pr/src/io/prlog.c b/pr/src/io/prlog.c new file mode 100644 index 00000000..f541a036 --- /dev/null +++ b/pr/src/io/prlog.c @@ -0,0 +1,504 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* + * 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 the Netscape Portable Runtime (NSPR). + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * Contributors: + * + * This Original Code has been modified by IBM Corporation. + * Modifications made by IBM described herein are + * Copyright (c) International Business Machines Corporation, 2000. + * Modifications to Mozilla code or documentation identified per + * MPL Section 3.3 + * + * Date Modified by Description of modification + * 04/10/2000 IBM Corp. Added DebugBreak() definitions for OS/2 + */ + +#include "primpl.h" +#include "prenv.h" +#include "prprf.h" +#include <string.h> + +/* + * Lock used to lock the log. + * + * We can't define _PR_LOCK_LOG simply as PR_Lock because PR_Lock may + * contain assertions. We have to avoid assertions in _PR_LOCK_LOG + * because PR_ASSERT calls PR_LogPrint, which in turn calls _PR_LOCK_LOG. + * This can lead to infinite recursion. + */ +static PRLock *_pr_logLock; +#if defined(_PR_PTHREADS) || defined(_PR_BTHREADS) +#define _PR_LOCK_LOG() PR_Lock(_pr_logLock); +#define _PR_UNLOCK_LOG() PR_Unlock(_pr_logLock); +#elif defined(_PR_GLOBAL_THREADS_ONLY) +#define _PR_LOCK_LOG() { _PR_LOCK_LOCK(_pr_logLock) +#define _PR_UNLOCK_LOG() _PR_LOCK_UNLOCK(_pr_logLock); } +#else + +#define _PR_LOCK_LOG() \ +{ \ + PRIntn _is; \ + PRThread *_me = _PR_MD_CURRENT_THREAD(); \ + if (!_PR_IS_NATIVE_THREAD(_me)) \ + _PR_INTSOFF(_is); \ + _PR_LOCK_LOCK(_pr_logLock) + +#define _PR_UNLOCK_LOG() \ + _PR_LOCK_UNLOCK(_pr_logLock); \ + PR_ASSERT(_me == _PR_MD_CURRENT_THREAD()); \ + if (!_PR_IS_NATIVE_THREAD(_me)) \ + _PR_INTSON(_is); \ +} + +#endif + +#if defined(XP_PC) +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif + +/* + * On NT, we can't define _PUT_LOG as PR_Write or _PR_MD_WRITE, + * because every asynchronous file io operation leads to a fiber context + * switch. So we define _PUT_LOG as fputs (from stdio.h). A side + * benefit is that fputs handles the LF->CRLF translation. This + * code can also be used on other platforms with file stream io. + */ +#if defined(WIN32) || defined(XP_OS2) +#define _PR_USE_STDIO_FOR_LOGGING +#endif + +/* +** Coerce Win32 log output to use OutputDebugString() when +** NSPR_LOG_FILE is set to "WinDebug". +*/ +#if defined(XP_PC) +#define WIN32_DEBUG_FILE (FILE*)-2 +#endif + +/* Macros used to reduce #ifdef pollution */ + +#if defined(_PR_USE_STDIO_FOR_LOGGING) +#define _PUT_LOG(fd, buf, nb) {fputs(buf, fd); fflush(fd);} +#elif defined(_PR_PTHREADS) +#define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb) +#elif defined(XP_MAC) +#define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE_SYNC(fd, buf, nb) +#else +#define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb) +#endif + +/************************************************************************/ + +static PRLogModuleInfo *logModules; + +static char *logBuf = NULL; +static char *logp; +static char *logEndp; +#ifdef _PR_USE_STDIO_FOR_LOGGING +static FILE *logFile = NULL; +#else +static PRFileDesc *logFile = 0; +#endif + +#define LINE_BUF_SIZE 512 +#define DEFAULT_BUF_SIZE 16384 + +#ifdef _PR_NEED_STRCASECMP + +/* + * strcasecmp is defined in /usr/ucblib/libucb.a on some platforms + * such as NCR and Unixware. Linking with both libc and libucb + * may cause some problem, so I just provide our own implementation + * of strcasecmp here. + */ + +static const unsigned char uc[] = +{ + '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', + '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017', + '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027', + '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '{', '|', '}', '~', '\177' +}; + +PRIntn strcasecmp(const char *a, const char *b) +{ + const unsigned char *ua = (const unsigned char *)a; + const unsigned char *ub = (const unsigned char *)b; + + if( ((const char *)0 == a) || (const char *)0 == b ) + return (PRIntn)(a-b); + + while( (uc[*ua] == uc[*ub]) && ('\0' != *a) ) + { + a++; + ua++; + ub++; + } + + return (PRIntn)(uc[*ua] - uc[*ub]); +} + +#endif /* _PR_NEED_STRCASECMP */ + +void _PR_InitLog(void) +{ + char *ev; + + _pr_logLock = PR_NewLock(); + + ev = PR_GetEnv("NSPR_LOG_MODULES"); + if (ev && ev[0]) { + char module[64]; + PRBool isSync = PR_FALSE; + PRIntn evlen = strlen(ev), pos = 0; + PRInt32 bufSize = DEFAULT_BUF_SIZE; + while (pos < evlen) { + PRIntn level = 1, count = 0, delta = 0; + count = sscanf(&ev[pos], "%64[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789]%n:%d%n", + module, &delta, &level, &delta); + pos += delta; + if (count == 0) break; + + /* + ** If count == 2, then we got module and level. If count + ** == 1, then level defaults to 1 (module enabled). + */ + if (strcasecmp(module, "sync") == 0) { + isSync = PR_TRUE; + } else if (strcasecmp(module, "bufsize") == 0) { + if (level >= LINE_BUF_SIZE) { + bufSize = level; + } + } else { + PRLogModuleInfo *lm = logModules; + PRBool skip_modcheck = + (0 == strcasecmp (module, "all")) ? PR_TRUE : PR_FALSE; + + while (lm != NULL) { + if (skip_modcheck) lm -> level = (PRLogModuleLevel)level; + else if (strcasecmp(module, lm->name) == 0) { + lm->level = (PRLogModuleLevel)level; + break; + } + lm = lm->next; + } + } + /*found:*/ + count = sscanf(&ev[pos], " , %n", &delta); + pos += delta; + if (count == -1) break; + } + PR_SetLogBuffering(isSync ? bufSize : 0); + + ev = PR_GetEnv("NSPR_LOG_FILE"); + if (ev && ev[0]) { + if (!PR_SetLogFile(ev)) { +#ifdef XP_PC + char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev); + if (str) { + OutputDebugString(str); + PR_smprintf_free(str); + } +#else + fprintf(stderr, "Unable to create nspr log file '%s'\n", ev); +#endif + } + } else { +#ifdef _PR_USE_STDIO_FOR_LOGGING + logFile = stderr; +#else + logFile = _pr_stderr; +#endif + } + } +} + +void _PR_LogCleanup(void) +{ + PR_LogFlush(); + +#ifdef _PR_USE_STDIO_FOR_LOGGING + if (logFile && logFile != stdout && logFile != stderr) { + fclose(logFile); + } +#else + if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) { + PR_Close(logFile); + } +#endif +} + +static void _PR_SetLogModuleLevel( PRLogModuleInfo *lm ) +{ + char *ev; + + ev = PR_GetEnv("NSPR_LOG_MODULES"); + if (ev && ev[0]) { + char module[64]; + PRIntn evlen = strlen(ev), pos = 0; + while (pos < evlen) { + PRIntn level = 1, count = 0, delta = 0; + + count = sscanf(&ev[pos], "%64[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789]%n:%d%n", + module, &delta, &level, &delta); + pos += delta; + if (count == 0) break; + + /* + ** If count == 2, then we got module and level. If count + ** == 1, then level defaults to 1 (module enabled). + */ + if (lm != NULL) + { + if ((strcasecmp(module, "all") == 0) + || (strcasecmp(module, lm->name) == 0)) + { + lm->level = (PRLogModuleLevel)level; + } + } + count = sscanf(&ev[pos], " , %n", &delta); + pos += delta; + if (count == -1) break; + } + } +} /* end _PR_SetLogModuleLevel() */ + +PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char *name) +{ + PRLogModuleInfo *lm; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + lm = PR_NEWZAP(PRLogModuleInfo); + if (lm) { + lm->name = strdup(name); + lm->level = PR_LOG_NONE; + lm->next = logModules; + logModules = lm; + } + _PR_SetLogModuleLevel(lm); + return lm; +} + +PR_IMPLEMENT(PRBool) PR_SetLogFile(const char *file) +{ +#ifdef _PR_USE_STDIO_FOR_LOGGING + FILE *newLogFile; + +#ifdef XP_PC + if ( strcmp( file, "WinDebug") == 0) + { + logFile = WIN32_DEBUG_FILE; + return(PR_TRUE); + } +#endif + newLogFile = fopen(file, "w"); + if (newLogFile) { + /* We do buffering ourselves. */ + setvbuf(newLogFile, NULL, _IONBF, 0); + if (logFile && logFile != stdout && logFile != stderr) { + fclose(logFile); + } + logFile = newLogFile; + } + return (PRBool) (newLogFile != 0); +#else + PRFileDesc *newLogFile; + + newLogFile = PR_Open(file, PR_WRONLY|PR_CREATE_FILE, 0666); + if (newLogFile) { + if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) { + PR_Close(logFile); + } + logFile = newLogFile; +#if defined(XP_MAC) + SetLogFileTypeCreator(file); +#endif + } + return (PRBool) (newLogFile != 0); +#endif /* _PR_USE_STDIO_FOR_LOGGING */ +} + +PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size) +{ + PR_LogFlush(); + + if (logBuf) + PR_DELETE(logBuf); + logBuf = 0; + + if (buffer_size >= LINE_BUF_SIZE) { + logp = logBuf = (char*) PR_MALLOC(buffer_size); + logEndp = logp + buffer_size; + } +} + +PR_IMPLEMENT(void) PR_LogPrint(const char *fmt, ...) +{ + va_list ap; + char line[LINE_BUF_SIZE]; + PRUint32 nb; + PRThread *me; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (!logFile) { + return; + } + + va_start(ap, fmt); + me = PR_GetCurrentThread(); + nb = PR_snprintf(line, sizeof(line)-1, "%ld[%p]: ", +#if defined(_PR_DCETHREADS) + /* The problem is that for _PR_DCETHREADS, pthread_t is not a + * pointer, but a structure; so you can't easily print it... + */ + me ? &(me->id): 0L, me); +#elif defined(_PR_BTHREADS) + me, me); +#else + me ? me->id : 0L, me); +#endif + + nb += PR_vsnprintf(line+nb, sizeof(line)-nb-1, fmt, ap); + if (nb && (line[nb-1] != '\n')) { +#ifndef XP_MAC + line[nb++] = '\n'; +#else + line[nb++] = '\015'; +#endif + line[nb] = '\0'; + } else { +#ifdef XP_MAC + line[nb-1] = '\015'; +#endif + } + va_end(ap); + + _PR_LOCK_LOG(); + if (logBuf == 0) { +#ifdef XP_PC + if ( logFile == WIN32_DEBUG_FILE) + OutputDebugString( line ); + else + _PUT_LOG(logFile, line, nb); +#else + _PUT_LOG(logFile, line, nb); +#endif + } else { + if (logp + nb > logEndp) { + _PUT_LOG(logFile, logBuf, logp - logBuf); + logp = logBuf; + } + memcpy(logp, line, nb); + logp += nb; + } + _PR_UNLOCK_LOG(); + PR_LogFlush(); +} + +PR_IMPLEMENT(void) PR_LogFlush(void) +{ + if (logBuf && logFile) { + _PR_LOCK_LOG(); + if (logp > logBuf) { + _PUT_LOG(logFile, logBuf, logp - logBuf); + logp = logBuf; + } + _PR_UNLOCK_LOG(); + } +} + +PR_IMPLEMENT(void) PR_Abort(void) +{ + PR_LogPrint("Aborting"); + abort(); +} + +#if defined(XP_OS2) +/* + * Added definitions for DebugBreak() for 2 different OS/2 compilers. + * Doing the int3 on purpose for Visual Age so that a developer can + * step over the instruction if so desired. Not always possible if + * trapping due to exception handling IBM-AKR + */ +#if defined(XP_OS2_VACPP) +#include <builtin.h> +static void DebugBreak(void) { _interrupt(3); } +#elif defined(XP_OS2_EMX) +/* Force a trap */ +static void DebugBreak(void) { int *pTrap=NULL; *pTrap = 1; } +#else +static void DebugBreak(void) { } +#endif +#endif /* XP_OS2 */ + +PR_IMPLEMENT(void) PR_Assert(const char *s, const char *file, PRIntn ln) +{ + PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln); +#if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS) + fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); +#endif +#ifdef XP_MAC + dprintf("Assertion failure: %s, at %s:%d\n", s, file, ln); +#endif +#if defined(WIN32) || defined(XP_OS2) + DebugBreak(); +#endif +#ifndef XP_MAC + abort(); +#endif +} + +#ifdef XP_MAC +PR_IMPLEMENT(void) PR_Init_Log(void) +{ + _PR_InitLog(); +} +#endif diff --git a/pr/src/io/prsocket.c b/pr/src/io/prsocket.c new file mode 100644 index 00000000..2483f963 --- /dev/null +++ b/pr/src/io/prsocket.c @@ -0,0 +1,1805 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * 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 the Netscape Portable Runtime (NSPR). + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include "primpl.h" + +#include <string.h> + +/************************************************************************/ + +/* These two functions are only used in assertions. */ +#if defined(DEBUG) + +PRBool IsValidNetAddr(const PRNetAddr *addr) +{ + if ((addr != NULL) +#ifdef XP_UNIX + && (addr->raw.family != PR_AF_LOCAL) +#endif + && (addr->raw.family != PR_AF_INET6) + && (addr->raw.family != PR_AF_INET)) { + return PR_FALSE; + } + return PR_TRUE; +} + +static PRBool IsValidNetAddrLen(const PRNetAddr *addr, PRInt32 addr_len) +{ + /* + * The definition of the length of a Unix domain socket address + * is not uniform, so we don't check it. + */ + if ((addr != NULL) +#ifdef XP_UNIX + && (addr->raw.family != AF_UNIX) +#endif + && (PR_NETADDR_SIZE(addr) != addr_len)) { +#if defined(LINUX) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 1 + /* + * In glibc 2.1, struct sockaddr_in6 is 24 bytes. In glibc 2.2 + * and in the 2.4 kernel, struct sockaddr_in6 has the scope_id + * field and is 28 bytes. It is possible for socket functions + * to return an addr_len greater than sizeof(struct sockaddr_in6). + * We need to allow that. (Bugzilla bug #77264) + */ + if ((PR_AF_INET6 == addr->raw.family) + && (sizeof(addr->ipv6) == addr_len)) { + return PR_TRUE; + } +#endif + /* + * The accept(), getsockname(), etc. calls on some platforms + * do not set the actual socket address length on return. + * In this case, we verifiy addr_len is still the value we + * passed in (i.e., sizeof(PRNetAddr)). + */ +#if defined(QNX) + if (sizeof(PRNetAddr) == addr_len) { + return PR_TRUE; + } +#endif + return PR_FALSE; + } + return PR_TRUE; +} + +#endif /* DEBUG */ + +static PRInt32 PR_CALLBACK SocketWritev(PRFileDesc *fd, const PRIOVec *iov, +PRInt32 iov_size, PRIntervalTime timeout) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + int w = 0; + const PRIOVec *tmp_iov; +#define LOCAL_MAXIOV 8 + PRIOVec local_iov[LOCAL_MAXIOV]; + PRIOVec *iov_copy = NULL; + int tmp_out; + int index, iov_cnt; + int count=0, sz = 0; /* 'count' is the return value. */ + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + /* + * Assume the first writev will succeed. Copy iov's only on + * failure. + */ + tmp_iov = iov; + for (index = 0; index < iov_size; index++) + sz += iov[index].iov_len; + + iov_cnt = iov_size; + + while (sz > 0) { + + w = _PR_MD_WRITEV(fd, tmp_iov, iov_cnt, timeout); + if (w < 0) { + count = -1; + break; + } + count += w; + if (fd->secret->nonblocking) { + break; + } + sz -= w; + + if (sz > 0) { + /* find the next unwritten vector */ + for ( index = 0, tmp_out = count; + tmp_out >= iov[index].iov_len; + tmp_out -= iov[index].iov_len, index++){;} /* nothing to execute */ + + if (tmp_iov == iov) { + /* + * The first writev failed so we + * must copy iov's around. + * Avoid calloc/free if there + * are few enough iov's. + */ + if (iov_size - index <= LOCAL_MAXIOV) + iov_copy = local_iov; + else if ((iov_copy = (PRIOVec *) PR_CALLOC((iov_size - index) * + sizeof *iov_copy)) == NULL) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + tmp_iov = iov_copy; + } + + PR_ASSERT(tmp_iov == iov_copy); + + /* fill in the first partial read */ + iov_copy[0].iov_base = &(((char *)iov[index].iov_base)[tmp_out]); + iov_copy[0].iov_len = iov[index].iov_len - tmp_out; + index++; + + /* copy the remaining vectors */ + for (iov_cnt=1; index<iov_size; iov_cnt++, index++) { + iov_copy[iov_cnt].iov_base = iov[index].iov_base; + iov_copy[iov_cnt].iov_len = iov[index].iov_len; + } + } + } + + if (iov_copy != local_iov) + PR_DELETE(iov_copy); + return count; +} + +/************************************************************************/ + +PR_IMPLEMENT(PRFileDesc *) PR_ImportTCPSocket(PRInt32 osfd) +{ +PRFileDesc *fd; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); + if (fd != NULL) { + _PR_MD_MAKE_NONBLOCK(fd); + _PR_MD_INIT_FD_INHERITABLE(fd, PR_TRUE); + } else + _PR_MD_CLOSE_SOCKET(osfd); + return(fd); +} + +PR_IMPLEMENT(PRFileDesc *) PR_ImportUDPSocket(PRInt32 osfd) +{ +PRFileDesc *fd; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods()); + if (fd != NULL) { + _PR_MD_MAKE_NONBLOCK(fd); + _PR_MD_INIT_FD_INHERITABLE(fd, PR_TRUE); + } else + _PR_MD_CLOSE_SOCKET(osfd); + return(fd); +} + + +static const PRIOMethods* PR_GetSocketPollFdMethods(void); + +PR_IMPLEMENT(PRFileDesc*) PR_CreateSocketPollFd(PRInt32 osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + fd = _PR_Getfd(); + + if (fd == NULL) PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + else + { + fd->secret->md.osfd = osfd; + fd->secret->inheritable = _PR_TRI_FALSE; + fd->secret->state = _PR_FILEDESC_OPEN; + fd->methods = PR_GetSocketPollFdMethods(); + } + + return fd; +} /* PR_CreateSocketPollFD */ + +PR_IMPLEMENT(PRStatus) PR_DestroySocketPollFd(PRFileDesc *fd) +{ + if (NULL == fd) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + fd->secret->state = _PR_FILEDESC_CLOSED; + _PR_Putfd(fd); + return PR_SUCCESS; +} /* PR_DestroySocketPollFd */ + +static PRStatus PR_CALLBACK SocketConnect( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRInt32 rv; /* Return value of _PR_MD_CONNECT */ + const PRNetAddr *addrp = addr; +#if defined(_PR_INET6) + PRNetAddr addrCopy; +#endif + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return PR_FAILURE; + } +#if defined(_PR_INET6) + if (addr->raw.family == PR_AF_INET6) { + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; + } +#endif + + rv = _PR_MD_CONNECT(fd, addrp, PR_NETADDR_SIZE(addr), timeout); + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("connect -> %d", rv)); + if (rv == 0) + return PR_SUCCESS; + else + return PR_FAILURE; +} + +static PRStatus PR_CALLBACK SocketConnectContinue( + PRFileDesc *fd, PRInt16 out_flags) +{ + PRInt32 osfd; + int err; + + if (out_flags & PR_POLL_NVAL) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + if ((out_flags & (PR_POLL_WRITE | PR_POLL_EXCEPT | PR_POLL_ERR)) == 0) { + PR_ASSERT(out_flags == 0); + PR_SetError(PR_IN_PROGRESS_ERROR, 0); + return PR_FAILURE; + } + + osfd = fd->secret->md.osfd; + +#if defined(XP_UNIX) + + err = _MD_unix_get_nonblocking_connect_error(osfd); + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; + +#elif defined(WIN32) || defined(WIN16) + +#if defined(WIN32) + /* + * The sleep circumvents a bug in Win32 WinSock. + * See Microsoft Knowledge Base article ID: Q165989. + */ + Sleep(0); +#endif /* WIN32 */ + + if (out_flags & PR_POLL_EXCEPT) { + int len = sizeof(err); + if (getsockopt(osfd, (int)SOL_SOCKET, SO_ERROR, (char *) &err, &len) + == SOCKET_ERROR) { + _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError()); + return PR_FAILURE; + } + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + } else { + PR_SetError(PR_UNKNOWN_ERROR, 0); + } + return PR_FAILURE; + } + + PR_ASSERT(out_flags & PR_POLL_WRITE); + return PR_SUCCESS; + +#elif defined(XP_OS2) + + if (out_flags & PR_POLL_EXCEPT) { + int len = sizeof(err); + if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char *) &err, &len) + < 0) { + _PR_MD_MAP_GETSOCKOPT_ERROR(sock_errno()); + return PR_FAILURE; + } + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + } else { + PR_SetError(PR_UNKNOWN_ERROR, 0); + } + return PR_FAILURE; + } + + PR_ASSERT(out_flags & PR_POLL_WRITE); + return PR_SUCCESS; + +#elif defined(XP_MAC) + + err = _MD_mac_get_nonblocking_connect_error(osfd); + if (err == -1) + return PR_FAILURE; + else + return PR_SUCCESS; + +#elif defined(XP_BEOS) + + err = _MD_beos_get_nonblocking_connect_error(fd); + if( err != 0 ) { + _PR_MD_MAP_CONNECT_ERROR(err); + return PR_FAILURE; + } + else + return PR_SUCCESS; + +#else + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +#endif +} + +PR_IMPLEMENT(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd) +{ + /* Find the NSPR layer and invoke its connectcontinue method */ + PRFileDesc *bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + + if (NULL == bottom) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + return SocketConnectContinue(bottom, pd->out_flags); +} + +static PRFileDesc* PR_CALLBACK SocketAccept(PRFileDesc *fd, PRNetAddr *addr, +PRIntervalTime timeout) +{ + PRInt32 osfd; + PRFileDesc *fd2; + PRUint32 al; + PRThread *me = _PR_MD_CURRENT_THREAD(); +#ifdef WINNT + PRNetAddr addrCopy; +#endif + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return 0; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return 0; + } + +#ifdef WINNT + if (addr == NULL) { + addr = &addrCopy; + } +#endif + al = sizeof(PRNetAddr); + osfd = _PR_MD_ACCEPT(fd, addr, &al, timeout); + if (osfd == -1) + return 0; + + fd2 = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); + if (!fd2) { + _PR_MD_CLOSE_SOCKET(osfd); + return NULL; + } + + fd2->secret->nonblocking = fd->secret->nonblocking; + fd2->secret->inheritable = fd->secret->inheritable; +#ifdef WINNT + if (!fd2->secret->nonblocking && fd2->secret->inheritable != _PR_TRI_TRUE) { + /* + * The new socket has been associated with an I/O + * completion port. There is no going back. + */ + fd2->secret->md.io_model_committed = PR_TRUE; + } + PR_ASSERT(al == PR_NETADDR_SIZE(addr)); + fd2->secret->md.accepted_socket = PR_TRUE; + memcpy(&fd2->secret->md.peer_addr, addr, al); +#endif + + /* + * On some platforms, the new socket created by accept() + * inherits the nonblocking (or overlapped io) attribute + * of the listening socket. As an optimization, these + * platforms can skip the following _PR_MD_MAKE_NONBLOCK + * call. + * + * On Mac, we MUST make this call, because _PR_MD_MAKE_NONBLOCK + * (which maps to _MD_makenonblock, see macsockotpt.c) + * installs the async notifier routine needed to make blocking + * I/O work properly. + */ +#if !defined(SOLARIS) && !defined(IRIX) && !defined(WINNT) + _PR_MD_MAKE_NONBLOCK(fd2); +#endif + +#ifdef _PR_INET6 + if (addr && (AF_INET6 == addr->raw.family)) + addr->raw.family = PR_AF_INET6; +#endif + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + PR_ASSERT(IsValidNetAddrLen(addr, al) == PR_TRUE); + + return fd2; +} + +#ifdef WINNT +PR_IMPLEMENT(PRFileDesc*) PR_NTFast_Accept(PRFileDesc *fd, PRNetAddr *addr, +PRIntervalTime timeout) +{ + PRInt32 osfd; + PRFileDesc *fd2; + PRIntn al; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRNetAddr addrCopy; + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return 0; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return 0; + } + + if (addr == NULL) { + addr = &addrCopy; + } + al = PR_NETADDR_SIZE(addr); + osfd = _PR_MD_FAST_ACCEPT(fd, addr, &al, timeout, PR_TRUE, NULL, NULL); + if (osfd == -1) { + return 0; + } + + fd2 = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); + if (!fd2) { + _PR_MD_CLOSE_SOCKET(osfd); + } else { + fd2->secret->nonblocking = fd->secret->nonblocking; + fd2->secret->md.io_model_committed = PR_TRUE; + PR_ASSERT(al == PR_NETADDR_SIZE(addr)); + fd2->secret->md.accepted_socket = PR_TRUE; + memcpy(&fd2->secret->md.peer_addr, addr, al); +#ifdef _PR_INET6 + if (AF_INET6 == addr->raw.family) + addr->raw.family = PR_AF_INET6; +#endif + } + return fd2; +} +#endif /* WINNT */ + + +static PRStatus PR_CALLBACK SocketBind(PRFileDesc *fd, const PRNetAddr *addr) +{ + PRInt32 result; + const PRNetAddr *addrp = addr; +#if defined(_PR_INET6) + PRNetAddr addrCopy; +#endif + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + +#ifdef XP_UNIX + if (addr->raw.family == AF_UNIX) { + /* Disallow relative pathnames */ + if (addr->local.path[0] != '/') { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + } +#endif /* XP_UNIX */ + +#if defined(_PR_INET6) + if (addr->raw.family == PR_AF_INET6) { + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; + } +#endif + result = _PR_MD_BIND(fd, addrp, PR_NETADDR_SIZE(addr)); + if (result < 0) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK SocketListen(PRFileDesc *fd, PRIntn backlog) +{ + PRInt32 result; + + result = _PR_MD_LISTEN(fd, backlog); + if (result < 0) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK SocketShutdown(PRFileDesc *fd, PRIntn how) +{ + PRInt32 result; + + result = _PR_MD_SHUTDOWN(fd, how); + if (result < 0) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRInt32 PR_CALLBACK SocketRecv(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, +PRIntervalTime timeout) +{ + PRInt32 rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if ((flags != 0) && (flags != PR_MSG_PEEK)) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("recv: fd=%p osfd=%d buf=%p amount=%d flags=%d", + fd, fd->secret->md.osfd, buf, amount, flags)); + +#ifdef _PR_HAVE_PEEK_BUFFER + if (fd->secret->peekBytes != 0) { + rv = (amount < fd->secret->peekBytes) ? + amount : fd->secret->peekBytes; + memcpy(buf, fd->secret->peekBuffer, rv); + if (flags == 0) { + /* consume the bytes in the peek buffer */ + fd->secret->peekBytes -= rv; + if (fd->secret->peekBytes != 0) { + memmove(fd->secret->peekBuffer, + fd->secret->peekBuffer + rv, + fd->secret->peekBytes); + } + } + return rv; + } + + /* allocate peek buffer, if necessary */ + if ((PR_MSG_PEEK == flags) && _PR_FD_NEED_EMULATE_MSG_PEEK(fd)) { + PR_ASSERT(0 == fd->secret->peekBytes); + /* impose a max size on the peek buffer */ + if (amount > _PR_PEEK_BUFFER_MAX) { + amount = _PR_PEEK_BUFFER_MAX; + } + if (fd->secret->peekBufSize < amount) { + if (fd->secret->peekBuffer) { + PR_Free(fd->secret->peekBuffer); + } + fd->secret->peekBufSize = amount; + fd->secret->peekBuffer = PR_Malloc(amount); + if (NULL == fd->secret->peekBuffer) { + fd->secret->peekBufSize = 0; + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + } + } +#endif + + rv = _PR_MD_RECV(fd, buf, amount, flags, timeout); + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("recv -> %d, error = %d, os error = %d", + rv, PR_GetError(), PR_GetOSError())); + +#ifdef _PR_HAVE_PEEK_BUFFER + if ((PR_MSG_PEEK == flags) && _PR_FD_NEED_EMULATE_MSG_PEEK(fd)) { + if (rv > 0) { + memcpy(fd->secret->peekBuffer, buf, rv); + fd->secret->peekBytes = rv; + } + } +#endif + + return rv; +} + +static PRInt32 PR_CALLBACK SocketRead(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + return SocketRecv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); +} + +static PRInt32 PR_CALLBACK SocketSend(PRFileDesc *fd, const void *buf, PRInt32 amount, +PRIntn flags, PRIntervalTime timeout) +{ + PRInt32 temp, count; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + count = 0; + while (amount > 0) { + PR_LOG(_pr_io_lm, PR_LOG_MAX, + ("send: fd=%p osfd=%d buf=%p amount=%d", + fd, fd->secret->md.osfd, buf, amount)); + temp = _PR_MD_SEND(fd, buf, amount, flags, timeout); + if (temp < 0) { + count = -1; + break; + } + + count += temp; + if (fd->secret->nonblocking) { + break; + } + buf = (const void*) ((const char*)buf + temp); + + amount -= temp; + } + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("send -> %d", count)); + return count; +} + +static PRInt32 PR_CALLBACK SocketWrite(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + return SocketSend(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); +} + +static PRStatus PR_CALLBACK SocketClose(PRFileDesc *fd) +{ + if (!fd || !fd->secret + || (fd->secret->state != _PR_FILEDESC_OPEN + && fd->secret->state != _PR_FILEDESC_CLOSED)) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + + if (fd->secret->state == _PR_FILEDESC_OPEN) { + if (_PR_MD_CLOSE_SOCKET(fd->secret->md.osfd) < 0) { + return PR_FAILURE; + } + fd->secret->state = _PR_FILEDESC_CLOSED; + } + +#ifdef _PR_HAVE_PEEK_BUFFER + if (fd->secret->peekBuffer) { + PR_ASSERT(fd->secret->peekBufSize > 0); + PR_DELETE(fd->secret->peekBuffer); + fd->secret->peekBufSize = 0; + fd->secret->peekBytes = 0; + } +#endif + + PR_FreeFileDesc(fd); + return PR_SUCCESS; +} + +static PRInt32 PR_CALLBACK SocketAvailable(PRFileDesc *fd) +{ + PRInt32 rv; +#ifdef _PR_HAVE_PEEK_BUFFER + if (fd->secret->peekBytes != 0) { + return fd->secret->peekBytes; + } +#endif + rv = _PR_MD_SOCKETAVAILABLE(fd); + return rv; +} + +static PRInt64 PR_CALLBACK SocketAvailable64(PRFileDesc *fd) +{ + PRInt64 rv; +#ifdef _PR_HAVE_PEEK_BUFFER + if (fd->secret->peekBytes != 0) { + LL_I2L(rv, fd->secret->peekBytes); + return rv; + } +#endif + LL_I2L(rv, _PR_MD_SOCKETAVAILABLE(fd)); + return rv; +} + +static PRStatus PR_CALLBACK SocketSync(PRFileDesc *fd) +{ +#if defined(XP_MAC) +#pragma unused (fd) +#endif + + return PR_SUCCESS; +} + +static PRInt32 PR_CALLBACK SocketSendTo( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRInt32 temp, count; + const PRNetAddr *addrp = addr; +#if defined(_PR_INET6) + PRNetAddr addrCopy; +#endif + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); +#if defined(_PR_INET6) + if (addr->raw.family == PR_AF_INET6) { + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; + } +#endif + + count = 0; + while (amount > 0) { + temp = _PR_MD_SENDTO(fd, buf, amount, flags, + addrp, PR_NETADDR_SIZE(addr), timeout); + if (temp < 0) { + count = -1; + break; + } + count += temp; + if (fd->secret->nonblocking) { + break; + } + buf = (const void*) ((const char*)buf + temp); + amount -= temp; + } + return count; +} + +static PRInt32 PR_CALLBACK SocketRecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, +PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRInt32 rv; + PRUint32 al; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + al = sizeof(PRNetAddr); + rv = _PR_MD_RECVFROM(fd, buf, amount, flags, addr, &al, timeout); +#ifdef _PR_INET6 + if (addr && (AF_INET6 == addr->raw.family)) + addr->raw.family = PR_AF_INET6; +#endif + return rv; +} + +static PRInt32 PR_CALLBACK SocketAcceptRead(PRFileDesc *sd, PRFileDesc **nd, +PRNetAddr **raddr, void *buf, PRInt32 amount, +PRIntervalTime timeout) +{ + PRInt32 rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + /* The socket must be in blocking mode. */ + if (sd->secret->nonblocking) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + *nd = NULL; + +#if defined(WINNT) + { + PRInt32 newSock; + PRNetAddr *raddrCopy; + + if (raddr == NULL) { + raddr = &raddrCopy; + } + rv = _PR_MD_ACCEPT_READ(sd, &newSock, raddr, buf, amount, timeout); + if (rv < 0) { + rv = -1; + } else { + /* Successfully accepted and read; create the new PRFileDesc */ + *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods()); + if (*nd == 0) { + _PR_MD_CLOSE_SOCKET(newSock); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + rv = -1; + } else { + (*nd)->secret->md.io_model_committed = PR_TRUE; + (*nd)->secret->md.accepted_socket = PR_TRUE; + memcpy(&(*nd)->secret->md.peer_addr, *raddr, + PR_NETADDR_SIZE(*raddr)); +#ifdef _PR_INET6 + if (AF_INET6 == *raddr->raw.family) + *raddr->raw.family = PR_AF_INET6; +#endif + } + } + } +#else + rv = PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout); +#endif + return rv; +} + +#ifdef WINNT +PR_IMPLEMENT(PRInt32) PR_NTFast_AcceptRead(PRFileDesc *sd, PRFileDesc **nd, +PRNetAddr **raddr, void *buf, PRInt32 amount, +PRIntervalTime timeout) +{ + PRInt32 rv; + PRInt32 newSock; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRNetAddr *raddrCopy; + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + *nd = NULL; + + if (raddr == NULL) { + raddr = &raddrCopy; + } + rv = _PR_MD_FAST_ACCEPT_READ(sd, &newSock, raddr, buf, amount, + timeout, PR_TRUE, NULL, NULL); + if (rv < 0) { + rv = -1; + } else { + /* Successfully accepted and read; create the new PRFileDesc */ + *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods()); + if (*nd == 0) { + _PR_MD_CLOSE_SOCKET(newSock); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + rv = -1; + } else { + (*nd)->secret->md.io_model_committed = PR_TRUE; + (*nd)->secret->md.accepted_socket = PR_TRUE; + memcpy(&(*nd)->secret->md.peer_addr, *raddr, + PR_NETADDR_SIZE(*raddr)); +#ifdef _PR_INET6 + if (AF_INET6 == *raddr->raw.family) + *raddr->raw.family = PR_AF_INET6; +#endif + } + } + return rv; +} + +PR_IMPLEMENT(PRInt32) PR_NTFast_AcceptRead_WithTimeoutCallback( +PRFileDesc *sd, PRFileDesc **nd, +PRNetAddr **raddr, void *buf, PRInt32 amount, +PRIntervalTime timeout, +_PR_AcceptTimeoutCallback callback, +void *callbackArg) +{ + PRInt32 rv; + PRInt32 newSock; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRNetAddr *raddrCopy; + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + *nd = NULL; + + if (raddr == NULL) { + raddr = &raddrCopy; + } + rv = _PR_MD_FAST_ACCEPT_READ(sd, &newSock, raddr, buf, amount, + timeout, PR_TRUE, callback, callbackArg); + if (rv < 0) { + rv = -1; + } else { + /* Successfully accepted and read; create the new PRFileDesc */ + *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods()); + if (*nd == 0) { + _PR_MD_CLOSE_SOCKET(newSock); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + rv = -1; + } else { + (*nd)->secret->md.io_model_committed = PR_TRUE; + (*nd)->secret->md.accepted_socket = PR_TRUE; + memcpy(&(*nd)->secret->md.peer_addr, *raddr, + PR_NETADDR_SIZE(*raddr)); +#ifdef _PR_INET6 + if (AF_INET6 == *raddr->raw.family) + *raddr->raw.family = PR_AF_INET6; +#endif + } + } + return rv; +} +#endif /* WINNT */ + +#ifdef WINNT +PR_IMPLEMENT(void) +PR_NTFast_UpdateAcceptContext(PRFileDesc *socket, PRFileDesc *acceptSocket) +{ + _PR_MD_UPDATE_ACCEPT_CONTEXT( + socket->secret->md.osfd, acceptSocket->secret->md.osfd); +} +#endif /* WINNT */ + +static PRInt32 PR_CALLBACK SocketSendFile( + PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + PRInt32 rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + /* The socket must be in blocking mode. */ + if (sd->secret->nonblocking) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } +#if defined(WINNT) + rv = _PR_MD_SENDFILE(sd, sfd, flags, timeout); + if ((rv >= 0) && (flags == PR_TRANSMITFILE_CLOSE_SOCKET)) { + /* + * This should be kept the same as SocketClose, except + * that _PR_MD_CLOSE_SOCKET(sd->secret->md.osfd) should + * not be called because the socket will be recycled. + */ + PR_FreeFileDesc(sd); + } +#else + rv = PR_EmulateSendFile(sd, sfd, flags, timeout); +#endif /* WINNT */ + + return rv; +} + +static PRInt32 PR_CALLBACK SocketTransmitFile(PRFileDesc *sd, PRFileDesc *fd, +const void *headers, PRInt32 hlen, PRTransmitFileFlags flags, +PRIntervalTime timeout) +{ + PRSendFileData sfd; + + sfd.fd = fd; + sfd.file_offset = 0; + sfd.file_nbytes = 0; + sfd.header = headers; + sfd.hlen = hlen; + sfd.trailer = NULL; + sfd.tlen = 0; + + return(SocketSendFile(sd, &sfd, flags, timeout)); +} + +static PRStatus PR_CALLBACK SocketGetName(PRFileDesc *fd, PRNetAddr *addr) +{ + PRInt32 result; + PRUint32 addrlen; + + addrlen = sizeof(PRNetAddr); + result = _PR_MD_GETSOCKNAME(fd, addr, &addrlen); + if (result < 0) { + return PR_FAILURE; + } +#ifdef _PR_INET6 + if (AF_INET6 == addr->raw.family) + addr->raw.family = PR_AF_INET6; +#endif + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + PR_ASSERT(IsValidNetAddrLen(addr, addrlen) == PR_TRUE); + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK SocketGetPeerName(PRFileDesc *fd, PRNetAddr *addr) +{ + PRInt32 result; + PRUint32 addrlen; + + addrlen = sizeof(PRNetAddr); + result = _PR_MD_GETPEERNAME(fd, addr, &addrlen); + if (result < 0) { + return PR_FAILURE; + } +#ifdef _PR_INET6 + if (AF_INET6 == addr->raw.family) + addr->raw.family = PR_AF_INET6; +#endif + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + PR_ASSERT(IsValidNetAddrLen(addr, addrlen) == PR_TRUE); + return PR_SUCCESS; +} + +static PRInt16 PR_CALLBACK SocketPoll( + PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) +{ +#ifdef XP_MAC +#pragma unused( fd, in_flags ) +#endif + *out_flags = 0; + return in_flags; +} /* SocketPoll */ + +static PRIOMethods tcpMethods = { + PR_DESC_SOCKET_TCP, + SocketClose, + SocketRead, + SocketWrite, + SocketAvailable, + SocketAvailable64, + SocketSync, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + SocketWritev, + SocketConnect, + SocketAccept, + SocketBind, + SocketListen, + SocketShutdown, + SocketRecv, + SocketSend, + (PRRecvfromFN)_PR_InvalidInt, + (PRSendtoFN)_PR_InvalidInt, + SocketPoll, + SocketAcceptRead, + SocketTransmitFile, + SocketGetName, + SocketGetPeerName, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + _PR_SocketGetSocketOption, + _PR_SocketSetSocketOption, + SocketSendFile, + SocketConnectContinue, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +static PRIOMethods udpMethods = { + PR_DESC_SOCKET_UDP, + SocketClose, + SocketRead, + SocketWrite, + SocketAvailable, + SocketAvailable64, + SocketSync, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + SocketWritev, + SocketConnect, + (PRAcceptFN)_PR_InvalidDesc, + SocketBind, + SocketListen, + SocketShutdown, + SocketRecv, + SocketSend, + SocketRecvFrom, + SocketSendTo, + SocketPoll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + SocketGetName, + SocketGetPeerName, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + _PR_SocketGetSocketOption, + _PR_SocketSetSocketOption, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + + +static PRIOMethods socketpollfdMethods = { + (PRDescType) 0, + (PRCloseFN)_PR_InvalidStatus, + (PRReadFN)_PR_InvalidInt, + (PRWriteFN)_PR_InvalidInt, + (PRAvailableFN)_PR_InvalidInt, + (PRAvailable64FN)_PR_InvalidInt64, + (PRFsyncFN)_PR_InvalidStatus, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + (PRWritevFN)_PR_InvalidInt, + (PRConnectFN)_PR_InvalidStatus, + (PRAcceptFN)_PR_InvalidDesc, + (PRBindFN)_PR_InvalidStatus, + (PRListenFN)_PR_InvalidStatus, + (PRShutdownFN)_PR_InvalidStatus, + (PRRecvFN)_PR_InvalidInt, + (PRSendFN)_PR_InvalidInt, + (PRRecvfromFN)_PR_InvalidInt, + (PRSendtoFN)_PR_InvalidInt, + SocketPoll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +PR_IMPLEMENT(const PRIOMethods*) PR_GetTCPMethods() +{ + return &tcpMethods; +} + +PR_IMPLEMENT(const PRIOMethods*) PR_GetUDPMethods() +{ + return &udpMethods; +} + +static const PRIOMethods* PR_GetSocketPollFdMethods() +{ + return &socketpollfdMethods; +} /* PR_GetSocketPollFdMethods */ + +#if !defined(_PR_INET6) || defined(_PR_INET6_PROBE) +PR_EXTERN(PRStatus) _pr_push_ipv6toipv4_layer(PRFileDesc *fd); + +#if defined(_PR_INET6_PROBE) + +PR_EXTERN(PRBool) _pr_ipv6_is_present; + +PR_IMPLEMENT(PRBool) _pr_test_ipv6_socket() +{ +PRInt32 osfd; + + osfd = _PR_MD_SOCKET(AF_INET6, SOCK_STREAM, 0); + if (osfd != -1) { + _PR_MD_CLOSE_SOCKET(osfd); + return PR_TRUE; + } + return PR_FALSE; +} +#endif /* _PR_INET6_PROBE */ + +#endif + +PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto) +{ + PRInt32 osfd; + PRFileDesc *fd; + PRInt32 tmp_domain = domain; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + if (PR_AF_INET != domain + && PR_AF_INET6 != domain +#if defined(XP_UNIX) + && PR_AF_LOCAL != domain +#endif + ) { + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); + return NULL; + } + +#if defined(_PR_INET6_PROBE) + if (PR_AF_INET6 == domain) { + if (_pr_ipv6_is_present == PR_FALSE) + domain = AF_INET; + else + domain = AF_INET6; + } +#elif defined(_PR_INET6) + if (PR_AF_INET6 == domain) + domain = AF_INET6; +#else + if (PR_AF_INET6 == domain) + domain = AF_INET; +#endif /* _PR_INET6 */ + osfd = _PR_MD_SOCKET(domain, type, proto); + if (osfd == -1) { + return 0; + } + if (type == SOCK_STREAM) + fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); + else + fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods()); + /* + * Make the sockets non-blocking + */ + if (fd != NULL) { + _PR_MD_MAKE_NONBLOCK(fd); + _PR_MD_INIT_FD_INHERITABLE(fd, PR_FALSE); +#if defined(_PR_INET6_PROBE) || !defined(_PR_INET6) + /* + * For platforms with no support for IPv6 + * create layered socket for IPv4-mapped IPv6 addresses + */ + if (PR_AF_INET6 == tmp_domain && PR_AF_INET == domain) { + if (PR_FAILURE == _pr_push_ipv6toipv4_layer(fd)) { + PR_Close(fd); + fd = NULL; + } + } +#endif + } else + _PR_MD_CLOSE_SOCKET(osfd); + + return fd; +} + +PR_IMPLEMENT(PRFileDesc *) PR_NewTCPSocket(void) +{ + PRInt32 domain = AF_INET; + + return PR_Socket(domain, SOCK_STREAM, 0); +} + +PR_IMPLEMENT(PRFileDesc*) PR_NewUDPSocket(void) +{ + PRInt32 domain = AF_INET; + + return PR_Socket(domain, SOCK_DGRAM, 0); +} + +PR_IMPLEMENT(PRFileDesc *) PR_OpenTCPSocket(PRIntn af) +{ + return PR_Socket(af, SOCK_STREAM, 0); +} + +PR_IMPLEMENT(PRFileDesc*) PR_OpenUDPSocket(PRIntn af) +{ + return PR_Socket(af, SOCK_DGRAM, 0); +} + +PR_IMPLEMENT(PRStatus) PR_NewTCPSocketPair(PRFileDesc *f[]) +{ +#ifdef XP_UNIX + PRInt32 rv, osfd[2]; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + rv = _PR_MD_SOCKETPAIR(AF_UNIX, SOCK_STREAM, 0, osfd); + if (rv == -1) { + return PR_FAILURE; + } + + f[0] = PR_AllocFileDesc(osfd[0], PR_GetTCPMethods()); + if (!f[0]) { + _PR_MD_CLOSE_SOCKET(osfd[0]); + _PR_MD_CLOSE_SOCKET(osfd[1]); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + return PR_FAILURE; + } + f[1] = PR_AllocFileDesc(osfd[1], PR_GetTCPMethods()); + if (!f[1]) { + PR_Close(f[0]); + _PR_MD_CLOSE_SOCKET(osfd[1]); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + return PR_FAILURE; + } + _PR_MD_MAKE_NONBLOCK(f[0]); + _PR_MD_INIT_FD_INHERITABLE(f[0], PR_FALSE); + _PR_MD_MAKE_NONBLOCK(f[1]); + _PR_MD_INIT_FD_INHERITABLE(f[1], PR_FALSE); + return PR_SUCCESS; +#elif defined(WINNT) + /* + * A socket pair is often used for interprocess communication, + * so we need to make sure neither socket is associated with + * the I/O completion port; otherwise it can't be used by a + * child process. + * + * The default implementation below cannot be used for NT + * because PR_Accept would have associated the I/O completion + * port with the listening and accepted sockets. + */ + SOCKET listenSock; + SOCKET osfd[2]; + struct sockaddr_in selfAddr; + int addrLen; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + osfd[0] = osfd[1] = INVALID_SOCKET; + listenSock = socket(AF_INET, SOCK_STREAM, 0); + if (listenSock == INVALID_SOCKET) { + goto failed; + } + selfAddr.sin_family = AF_INET; + selfAddr.sin_port = 0; + selfAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* BugZilla: 35408 */ + addrLen = sizeof(selfAddr); + if (bind(listenSock, (struct sockaddr *) &selfAddr, + addrLen) == SOCKET_ERROR) { + goto failed; + } + if (getsockname(listenSock, (struct sockaddr *) &selfAddr, + &addrLen) == SOCKET_ERROR) { + goto failed; + } + if (listen(listenSock, 5) == SOCKET_ERROR) { + goto failed; + } + osfd[0] = socket(AF_INET, SOCK_STREAM, 0); + if (osfd[0] == INVALID_SOCKET) { + goto failed; + } + selfAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* + * Only a thread is used to do the connect and accept. + * I am relying on the fact that connect returns + * successfully as soon as the connect request is put + * into the listen queue (but before accept is called). + * This is the behavior of the BSD socket code. If + * connect does not return until accept is called, we + * will need to create another thread to call connect. + */ + if (connect(osfd[0], (struct sockaddr *) &selfAddr, + addrLen) == SOCKET_ERROR) { + goto failed; + } + osfd[1] = accept(listenSock, NULL, NULL); + if (osfd[1] == INVALID_SOCKET) { + goto failed; + } + closesocket(listenSock); + + f[0] = PR_AllocFileDesc(osfd[0], PR_GetTCPMethods()); + if (!f[0]) { + closesocket(osfd[0]); + closesocket(osfd[1]); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + return PR_FAILURE; + } + f[1] = PR_AllocFileDesc(osfd[1], PR_GetTCPMethods()); + if (!f[1]) { + PR_Close(f[0]); + closesocket(osfd[1]); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + return PR_FAILURE; + } + _PR_MD_INIT_FD_INHERITABLE(f[0], PR_FALSE); + _PR_MD_INIT_FD_INHERITABLE(f[1], PR_FALSE); + return PR_SUCCESS; + +failed: + if (listenSock != INVALID_SOCKET) { + closesocket(listenSock); + } + if (osfd[0] != INVALID_SOCKET) { + closesocket(osfd[0]); + } + if (osfd[1] != INVALID_SOCKET) { + closesocket(osfd[1]); + } + return PR_FAILURE; +#else /* not Unix or NT */ + /* + * default implementation + */ + PRFileDesc *listenSock; + PRNetAddr selfAddr; + PRUint16 port; + + f[0] = f[1] = NULL; + listenSock = PR_NewTCPSocket(); + if (listenSock == NULL) { + goto failed; + } + PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &selfAddr); /* BugZilla: 35408 */ + if (PR_Bind(listenSock, &selfAddr) == PR_FAILURE) { + goto failed; + } + if (PR_GetSockName(listenSock, &selfAddr) == PR_FAILURE) { + goto failed; + } + port = ntohs(selfAddr.inet.port); + if (PR_Listen(listenSock, 5) == PR_FAILURE) { + goto failed; + } + f[0] = PR_NewTCPSocket(); + if (f[0] == NULL) { + goto failed; + } + PR_InitializeNetAddr(PR_IpAddrLoopback, port, &selfAddr); + + /* + * Only a thread is used to do the connect and accept. + * I am relying on the fact that PR_Connect returns + * successfully as soon as the connect request is put + * into the listen queue (but before PR_Accept is called). + * This is the behavior of the BSD socket code. If + * connect does not return until accept is called, we + * will need to create another thread to call connect. + */ + if (PR_Connect(f[0], &selfAddr, PR_INTERVAL_NO_TIMEOUT) + == PR_FAILURE) { + goto failed; + } + f[1] = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT); + if (f[1] == NULL) { + goto failed; + } + PR_Close(listenSock); + return PR_SUCCESS; + +failed: + if (listenSock) { + PR_Close(listenSock); + } + if (f[0]) { + PR_Close(f[0]); + } + return PR_FAILURE; +#endif +} + +PR_IMPLEMENT(PRInt32) +PR_FileDesc2NativeHandle(PRFileDesc *fd) +{ + if (fd) { + /* + * The fd may be layered. Chase the links to the + * bottom layer to get the osfd. + */ + PRFileDesc *bottom = fd; + while (bottom->lower != NULL) { + bottom = bottom->lower; + } + return bottom->secret->md.osfd; + } else { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } +} + +PR_IMPLEMENT(void) +PR_ChangeFileDescNativeHandle(PRFileDesc *fd, PRInt32 handle) +{ + if (fd) + fd->secret->md.osfd = handle; +} + +/* +** Select compatibility +** +*/ + +PR_IMPLEMENT(void) PR_FD_ZERO(PR_fd_set *set) +{ + memset(set, 0, sizeof(PR_fd_set)); +} + +PR_IMPLEMENT(void) PR_FD_SET(PRFileDesc *fh, PR_fd_set *set) +{ + PR_ASSERT( set->hsize < PR_MAX_SELECT_DESC ); + + set->harray[set->hsize++] = fh; +} + +PR_IMPLEMENT(void) PR_FD_CLR(PRFileDesc *fh, PR_fd_set *set) +{ + PRUint32 index, index2; + + for (index = 0; index<set->hsize; index++) + if (set->harray[index] == fh) { + for (index2=index; index2 < (set->hsize-1); index2++) { + set->harray[index2] = set->harray[index2+1]; + } + set->hsize--; + break; + } +} + +PR_IMPLEMENT(PRInt32) PR_FD_ISSET(PRFileDesc *fh, PR_fd_set *set) +{ + PRUint32 index; + for (index = 0; index<set->hsize; index++) + if (set->harray[index] == fh) { + return 1; + } + return 0; +} + +PR_IMPLEMENT(void) PR_FD_NSET(PRInt32 fd, PR_fd_set *set) +{ + PR_ASSERT( set->nsize < PR_MAX_SELECT_DESC ); + + set->narray[set->nsize++] = fd; +} + +PR_IMPLEMENT(void) PR_FD_NCLR(PRInt32 fd, PR_fd_set *set) +{ + PRUint32 index, index2; + + for (index = 0; index<set->nsize; index++) + if (set->narray[index] == fd) { + for (index2=index; index2 < (set->nsize-1); index2++) { + set->narray[index2] = set->narray[index2+1]; + } + set->nsize--; + break; + } +} + +PR_IMPLEMENT(PRInt32) PR_FD_NISSET(PRInt32 fd, PR_fd_set *set) +{ + PRUint32 index; + for (index = 0; index<set->nsize; index++) + if (set->narray[index] == fd) { + return 1; + } + return 0; +} + + +#if !defined(NEED_SELECT) +#if !defined(XP_MAC) +#include "obsolete/probslet.h" +#else +#include "probslet.h" +#endif + +#define PD_INCR 20 + +static PRPollDesc *_pr_setfd( + PR_fd_set *set, PRInt16 flags, PRPollDesc *polldesc) +{ + PRUintn fsidx, pdidx; + PRPollDesc *poll = polldesc; + + if (NULL == set) return poll; + + /* First set the pr file handle osfds */ + for (fsidx = 0; fsidx < set->hsize; fsidx++) + { + for (pdidx = 0; 1; pdidx++) + { + if ((PRFileDesc*)-1 == poll[pdidx].fd) + { + /* our vector is full - extend and condition it */ + poll = (PRPollDesc*)PR_Realloc( + poll, (pdidx + 1 + PD_INCR) * sizeof(PRPollDesc)); + if (NULL == poll) goto out_of_memory; + memset( + poll + pdidx * sizeof(PRPollDesc), + 0, PD_INCR * sizeof(PRPollDesc)); + poll[pdidx + PD_INCR].fd = (PRFileDesc*)-1; + } + if ((NULL == poll[pdidx].fd) + || (poll[pdidx].fd == set->harray[fsidx])) + { + /* PR_ASSERT(0 == (poll[pdidx].in_flags & flags)); */ + /* either empty or prevously defined */ + poll[pdidx].fd = set->harray[fsidx]; /* possibly redundant */ + poll[pdidx].in_flags |= flags; /* possibly redundant */ + break; + } + } + } + +#if 0 + /* Second set the native osfds */ + for (fsidx = 0; fsidx < set->nsize; fsidx++) + { + for (pdidx = 0; ((PRFileDesc*)-1 != poll[pdidx].fd); pdidx++) + { + if ((PRFileDesc*)-1 == poll[pdidx].fd) + { + /* our vector is full - extend and condition it */ + poll = PR_Realloc( + poll, (pdidx + PD_INCR) * sizeof(PRPollDesc)); + if (NULL == poll) goto out_of_memory; + memset( + poll + pdidx * sizeof(PRPollDesc), + 0, PD_INCR * sizeof(PRPollDesc)); + poll[(pdidx + PD_INCR)].fd = (PRFileDesc*)-1; + } + if ((NULL == poll[pdidx].fd) + || (poll[pdidx].fd == set->narray[fsidx])) + { + /* either empty or prevously defined */ + poll[pdidx].fd = set->narray[fsidx]; + PR_ASSERT(0 == (poll[pdidx].in_flags & flags)); + poll[pdidx].in_flags |= flags; + break; + } + } + } +#endif /* 0 */ + + return poll; + +out_of_memory: + if (NULL != polldesc) PR_DELETE(polldesc); + return NULL; +} /* _pr_setfd */ + +#endif /* !defined(NEED_SELECT) */ + +PR_IMPLEMENT(PRInt32) PR_Select( + PRInt32 unused, PR_fd_set *pr_rd, PR_fd_set *pr_wr, + PR_fd_set *pr_ex, PRIntervalTime timeout) +{ + +#if !defined(NEED_SELECT) + PRInt32 npds = 0; + /* + ** Find out how many fds are represented in the three lists. + ** Then allocate a polling descriptor for the logical union + ** (there can't be any overlapping) and call PR_Poll(). + */ + + PRPollDesc *copy, *poll; + + static PRBool warning = PR_TRUE; + if (warning) warning = _PR_Obsolete( "PR_Select()", "PR_Poll()"); + + /* try to get an initial guesss at how much space we need */ + npds = 0; + if ((NULL != pr_rd) && ((pr_rd->hsize + pr_rd->nsize - npds) > 0)) + npds = pr_rd->hsize + pr_rd->nsize; + if ((NULL != pr_wr) && ((pr_wr->hsize + pr_wr->nsize - npds) > 0)) + npds = pr_wr->hsize + pr_wr->nsize; + if ((NULL != pr_ex) && ((pr_ex->hsize + pr_ex->nsize - npds) > 0)) + npds = pr_ex->hsize + pr_ex->nsize; + + if (0 == npds) + { + PR_Sleep(timeout); + return 0; + } + + copy = poll = (PRPollDesc*)PR_Calloc(npds + PD_INCR, sizeof(PRPollDesc)); + if (NULL == poll) goto out_of_memory; + poll[npds + PD_INCR - 1].fd = (PRFileDesc*)-1; + + poll = _pr_setfd(pr_rd, PR_POLL_READ, poll); + if (NULL == poll) goto out_of_memory; + poll = _pr_setfd(pr_wr, PR_POLL_WRITE, poll); + if (NULL == poll) goto out_of_memory; + poll = _pr_setfd(pr_ex, PR_POLL_EXCEPT, poll); + if (NULL == poll) goto out_of_memory; + unused = 0; + while (NULL != poll[unused].fd && (PRFileDesc*)-1 != poll[unused].fd) + { + ++unused; + } + + PR_ASSERT(unused > 0); + npds = PR_Poll(poll, unused, timeout); + + if (npds > 0) + { + /* Copy the results back into the fd sets */ + if (NULL != pr_rd) pr_rd->nsize = pr_rd->hsize = 0; + if (NULL != pr_wr) pr_wr->nsize = pr_wr->hsize = 0; + if (NULL != pr_ex) pr_ex->nsize = pr_ex->hsize = 0; + for (copy = &poll[unused - 1]; copy >= poll; --copy) + { + if (copy->out_flags & PR_POLL_NVAL) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + npds = -1; + break; + } + if (copy->out_flags & PR_POLL_READ) + if (NULL != pr_rd) pr_rd->harray[pr_rd->hsize++] = copy->fd; + if (copy->out_flags & PR_POLL_WRITE) + if (NULL != pr_wr) pr_wr->harray[pr_wr->hsize++] = copy->fd; + if (copy->out_flags & PR_POLL_EXCEPT) + if (NULL != pr_ex) pr_ex->harray[pr_ex->hsize++] = copy->fd; + } + } + PR_DELETE(poll); + + return npds; +out_of_memory: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + +#endif /* !defined(NEED_SELECT) */ + +} |