static int acceptNamedPipe() { int ipcFd = -1; #ifdef DEBUG_CGI FILE *dbg = fopen("c:\\libcgi1.log", "w"); int i=0; #endif #ifdef DEBUG_CGI fprintf(dbg,"%d\n",hListen); fclose(dbg); #endif if (! ConnectNamedPipe(hListen, NULL)) { switch (GetLastError()) { case ERROR_PIPE_CONNECTED: // A client connected after CreateNamedPipe but // before ConnectNamedPipe. Its a good connection. break; case ERROR_IO_PENDING: // The NamedPipe was opened with an Overlapped structure // and there is a pending io operation. mod_fastcgi // did this in 2.2.12 (fcgi_pm.c v1.52). case ERROR_PIPE_LISTENING: // The pipe handle is in nonblocking mode. case ERROR_NO_DATA: // The previous client closed its handle (and we failed // to call DisconnectNamedPipe) default: printLastError("unexpected ConnectNamedPipe() error"); } } ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int) hListen, -1); if (ipcFd == -1) { DisconnectNamedPipe(hListen); } return ipcFd; }
static int acceptNamedPipe() { int ipcFd = -1; if (! ConnectNamedPipe(hListen, NULL)) { switch (GetLastError()) { case ERROR_PIPE_CONNECTED: // A client connected after CreateNamedPipe but // before ConnectNamedPipe. Its a good connection. break; case ERROR_IO_PENDING: // The NamedPipe was opened with an Overlapped structure // and there is a pending io operation. mod_fastcgi // did this in 2.2.12 (fcgi_pm.c v1.52). case ERROR_PIPE_LISTENING: // The pipe handle is in nonblocking mode. case ERROR_NO_DATA: // The previous client closed its handle (and we failed // to call DisconnectNamedPipe) default: printLastError("unexpected ConnectNamedPipe() error"); } } ASSERT(INT_MIN <= (intptr_t)hListen && (intptr_t)hListen <= INT_MAX); ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)(intptr_t)hListen, -1); if (ipcFd == -1) { DisconnectNamedPipe(hListen); } return ipcFd; }
/* *---------------------------------------------------------------------- * * OS_FcgiConnect -- * * Create the pipe pathname connect to the remote application if * possible. * * Results: * -1 if fail or a valid handle if connection succeeds. * * Side effects: * Remote connection established. * *---------------------------------------------------------------------- */ int OS_FcgiConnect(char *bindPath) { short port = getPort(bindPath); int pseudoFd = -1; if (port) { struct hostent *hp; char *host = NULL; struct sockaddr_in sockAddr; int sockLen = sizeof(sockAddr); SOCKET sock; if (*bindPath != ':') { char * p = strchr(bindPath, ':'); int len = p - bindPath + 1; host = malloc(len); strncpy(host, bindPath, len); host[len] = '\0'; } hp = gethostbyname(host ? host : LOCALHOST); if (host) { free(host); } if (hp == NULL) { fprintf(stderr, "Unknown host: %s\n", bindPath); return -1; } memset(&sockAddr, 0, sizeof(sockAddr)); sockAddr.sin_family = AF_INET; memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length); sockAddr.sin_port = htons(port); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { return -1; } if (! connect(sock, (struct sockaddr *) &sockAddr, sockLen)) { closesocket(sock); return -1; } pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, sock, -1); if (pseudoFd == -1) { closesocket(sock); return -1; } } else { char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1); HANDLE hPipe; if (! pipePath) { return -1; } strcpy(pipePath, bindPathPrefix); strcat(pipePath, bindPath); hPipe = CreateFile(pipePath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); free(pipePath); if( hPipe == INVALID_HANDLE_VALUE) { return -1; } pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int) hPipe, -1); if (pseudoFd == -1) { CloseHandle(hPipe); return -1; } /* * Set stdin equal to our pseudo FD and create the I/O completion * port to be used for async I/O. */ if (! CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1)) { Win32FreeDescriptor(pseudoFd); CloseHandle(hPipe); return -1; } } return pseudoFd; }
/* * OS_CreateLocalIpcFd -- * * This procedure is responsible for creating the listener pipe * on Windows NT for local process communication. It will create a * named pipe and return a file descriptor to it to the caller. * * Results: * Listener pipe created. This call returns either a valid * pseudo file descriptor or -1 on error. * * Side effects: * Listener pipe and IPC address are stored in the FCGI info * structure. * 'errno' will set on errors (-1 is returned). * *---------------------------------------------------------------------- */ int OS_CreateLocalIpcFd(const char *bindPath, int backlog) { int pseudoFd = -1; short port = getPort(bindPath); if (acceptMutex == INVALID_HANDLE_VALUE) { acceptMutex = CreateMutex(NULL, FALSE, NULL); if (acceptMutex == NULL) return -2; if (! SetHandleInformation(acceptMutex, HANDLE_FLAG_INHERIT, TRUE)) return -3; } // There's nothing to be gained (at the moment) by a shutdown Event if (port && *bindPath != ':' && strncmp(bindPath, LOCALHOST, strlen(LOCALHOST))) { fprintf(stderr, "To start a service on a TCP port can not " "specify a host name.\n" "You should either use \"localhost:<port>\" or " " just use \":<port>.\"\n"); exit(1); } listenType = (port) ? FD_SOCKET_SYNC : FD_PIPE_ASYNC; if (port) { SOCKET listenSock; struct sockaddr_in sockAddr; int sockLen = sizeof(sockAddr); memset(&sockAddr, 0, sizeof(sockAddr)); sockAddr.sin_family = AF_INET; sockAddr.sin_addr.s_addr = htonl(INADDR_ANY); sockAddr.sin_port = htons(port); listenSock = socket(AF_INET, SOCK_STREAM, 0); if (listenSock == INVALID_SOCKET) { return -4; } if (bind(listenSock, (struct sockaddr *) &sockAddr, sockLen) ) { return -12; } if (listen(listenSock, backlog)) { return -5; } pseudoFd = Win32NewDescriptor(listenType, listenSock, -1); if (pseudoFd == -1) { closesocket(listenSock); return -6; } hListen = (HANDLE) listenSock; } else { HANDLE hListenPipe = INVALID_HANDLE_VALUE; char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1); if (! pipePath) { return -7; } strcpy(pipePath, bindPathPrefix); strcat(pipePath, bindPath); hListenPipe = CreateNamedPipe(pipePath, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE, PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, NULL); free(pipePath); if (hListenPipe == INVALID_HANDLE_VALUE) { return -8; } if (! SetHandleInformation(hListenPipe, HANDLE_FLAG_INHERIT, TRUE)) { return -9; } pseudoFd = Win32NewDescriptor(listenType, (int) hListenPipe, -1); if (pseudoFd == -1) { CloseHandle(hListenPipe); return -10; } hListen = (HANDLE) hListenPipe; } return pseudoFd; }
/* *-------------------------------------------------------------- * * OS_LibInit -- * * Set up the OS library for use. * * Results: * Returns 0 if success, -1 if not. * * Side effects: * Sockets initialized, pseudo file descriptors setup, etc. * *-------------------------------------------------------------- */ int OS_LibInit(int stdioFds[3]) { WORD wVersion; WSADATA wsaData; int err; int fakeFd; char *cLenPtr = NULL; char *val = NULL; if(libInitialized) return 0; InitializeCriticalSection(&fdTableCritical); /* * Initialize windows sockets library. */ wVersion = MAKEWORD(2,0); err = WSAStartup( wVersion, &wsaData ); if (err) { fprintf(stderr, "Error starting Windows Sockets. Error: %d", WSAGetLastError()); exit(111); } /* * Create the I/O completion port to be used for our I/O queue. */ if (hIoCompPort == INVALID_HANDLE_VALUE) { hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, 0, 1); if(hIoCompPort == INVALID_HANDLE_VALUE) { printf("<H2>OS_LibInit Failed CreateIoCompletionPort! ERROR: %d</H2>\r\n\r\n", GetLastError()); return -1; } } /* * If a shutdown event is in the env, save it (I don't see any to * remove it from the environment out from under the application). * Spawn a thread to wait on the shutdown request. */ val = getenv(SHUTDOWN_EVENT_NAME); if (val != NULL) { HANDLE shutdownEvent = (HANDLE) atoi(val); if (_beginthread(ShutdownRequestThread, 0, shutdownEvent) == -1) { return -1; } } if (acceptMutex == INVALID_HANDLE_VALUE) { /* If an accept mutex is in the env, use it */ val = getenv(MUTEX_VARNAME); if (val != NULL) { acceptMutex = (HANDLE) atoi(val); } } /* * Determine if this library is being used to listen for FastCGI * connections. This is communicated by STDIN containing a * valid handle to a listener object. In this case, both the * "stdout" and "stderr" handles will be INVALID (ie. closed) by * the starting process. * * The trick is determining if this is a pipe or a socket... * * XXX: Add the async accept test to determine socket or handle to a * pipe!!! */ if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) && (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) && (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) ) { DWORD pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT; HANDLE oldStdIn = GetStdHandle(STD_INPUT_HANDLE); // Move the handle to a "low" number if (! DuplicateHandle(GetCurrentProcess(), oldStdIn, GetCurrentProcess(), &hListen, 0, TRUE, DUPLICATE_SAME_ACCESS)) { return -1; } if (! SetStdHandle(STD_INPUT_HANDLE, hListen)) { return -1; } CloseHandle(oldStdIn); /* * Set the pipe handle state so that it operates in wait mode. * * NOTE: The listenFd is not mapped to a pseudo file descriptor * as all work done on it is contained to the OS library. * * XXX: Initial assumption is that SetNamedPipeHandleState will * fail if this is an IP socket... */ if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL)) { listenType = FD_PIPE_SYNC; } else { listenType = FD_SOCKET_SYNC; } } /* * If there are no stdioFds passed in, we're done. */ if(stdioFds == NULL) { libInitialized = 1; return 0; } /* * Setup standard input asynchronous I/O. There is actually a separate * thread spawned for this purpose. The reason for this is that some * web servers use anonymous pipes for the connection between itself * and a CGI application. Anonymous pipes can't perform asynchronous * I/O or use I/O completion ports. Therefore in order to present a * consistent I/O dispatch model to an application we emulate I/O * completion port behavior by having the standard input thread posting * messages to the hIoCompPort which look like a complete overlapped * I/O structure. This keeps the event dispatching simple from the * application perspective. */ stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE); if(!SetHandleInformation(stdioHandles[STDIN_FILENO], HANDLE_FLAG_INHERIT, 0)) { /* * XXX: Causes error when run from command line. Check KB err = GetLastError(); DebugBreak(); exit(99); */ } if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)stdioHandles[STDIN_FILENO], STDIN_FILENO)) == -1) { return -1; } else { /* * Set stdin equal to our pseudo FD and create the I/O completion * port to be used for async I/O. */ stdioFds[STDIN_FILENO] = fakeFd; } /* * Create the I/O completion port to be used for communicating with * the thread doing I/O on standard in. This port will carry read * and possibly thread termination requests to the StdinThread. */ if (hStdinCompPort == INVALID_HANDLE_VALUE) { hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, 0, 1); if(hStdinCompPort == INVALID_HANDLE_VALUE) { printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %d</H2>\r\n\r\n", GetLastError()); return -1; } } /* * Create the thread that will read stdin if the CONTENT_LENGTH * is non-zero. */ if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL && atoi(cLenPtr) > 0) { hStdinThread = (HANDLE) _beginthread(StdinThread, 0, NULL); if (hStdinThread == (HANDLE) -1) { printf("<H2>OS_LibInit Failed to create STDIN thread! ERROR: %d</H2>\r\n\r\n", GetLastError()); return -1; } } /* * STDOUT will be used synchronously. * * XXX: May want to convert this so that it could be used for OVERLAPPED * I/O later. If so, model it after the Stdin I/O as stdout is * also incapable of async I/O on some servers. */ stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE); if(!SetHandleInformation(stdioHandles[STDOUT_FILENO], HANDLE_FLAG_INHERIT, FALSE)) { DebugBreak(); exit(99); } if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)stdioHandles[STDOUT_FILENO], STDOUT_FILENO)) == -1) { return -1; } else { /* * Set stdout equal to our pseudo FD */ stdioFds[STDOUT_FILENO] = fakeFd; } stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE); if(!SetHandleInformation(stdioHandles[STDERR_FILENO], HANDLE_FLAG_INHERIT, FALSE)) { DebugBreak(); exit(99); } if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)stdioHandles[STDERR_FILENO], STDERR_FILENO)) == -1) { return -1; } else { /* * Set stderr equal to our pseudo FD */ stdioFds[STDERR_FILENO] = fakeFd; } return 0; }
static int acceptSocket(const char *webServerAddrs) { SOCKET hSock; int ipcFd = -1; for (;;) { struct sockaddr sockaddr; int sockaddrLen = sizeof(sockaddr); for (;;) { const struct timeval timeout = {1, 0}; fd_set readfds; FD_ZERO(&readfds); #pragma warning( disable : 4127 ) FD_SET((unsigned int) hListen, &readfds); #pragma warning( default : 4127 ) if (select(0, &readfds, NULL, NULL, &timeout) == 0) { if (shutdownPending) { OS_LibShutdown(); return -1; } } else { break; } } #if NO_WSAACEPT hSock = accept((SOCKET) hListen, &sockaddr, &sockaddrLen); if (hSock == INVALID_SOCKET) { break; } if (isAddrOK((struct sockaddr_in *) &sockaddr, webServerAddrs)) { break; } closesocket(hSock); #else hSock = WSAAccept((unsigned int) hListen, &sockaddr, &sockaddrLen, isAddrOKCallback, (DWORD) webServerAddrs); if (hSock != INVALID_SOCKET) { break; } if (WSAGetLastError() != WSAECONNREFUSED) { break; } #endif } if (hSock == INVALID_SOCKET) { /* Use FormatMessage() */ fprintf(stderr, "accept()/WSAAccept() failed: %d", WSAGetLastError()); return -1; } ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1); if (ipcFd == -1) { closesocket(hSock); } return ipcFd; }