static void bindSocketToAddr(SOCKET const winsock, const struct sockaddr * const addrP, socklen_t const sockAddrLen, const char ** const errorP) { int rc; rc = bind(winsock, (struct sockaddr *)addrP, sockAddrLen); if (rc != 0) { int const lastError = WSAGetLastError(); xmlrpc_asprintf(errorP, "Unable to bind socket to the socket address. " "bind() failed with WSAERROR %i (%s)", lastError, getWSAError(lastError)); } else *errorP = NULL; }
void SocketWinInit(const char ** const errorP) { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(1, 0); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { int const lastError = WSAGetLastError(); xmlrpc_asprintf(errorP, "WSAStartup() faild with error %d (%s)", lastError, getWSAError(lastError)); } else *errorP = NULL; }
static void setSocketOptions(SOCKET const fd, const char ** const errorP) { int32_t const n = 1; int rc; rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof(n)); if (rc != 0) { int const lastError = WSAGetLastError(); xmlrpc_asprintf(errorP, "Failed to set socket options. " "setsockopt() failed with WSAERROR %d (%s)", lastError, getWSAError(lastError)); } else *errorP = NULL; }
static void addAuthCookie(xmlrpc_env * const envP, TSession * const abyssSessionP, const char * const authCookie) { const char * cookieResponse; xmlrpc_asprintf(&cookieResponse, "auth=%s", authCookie); if (xmlrpc_strnomem(cookieResponse)) xmlrpc_faultf(envP, "Insufficient memory to generate cookie " "response header."); else { ResponseAddField(abyssSessionP, "Set-Cookie", cookieResponse); xmlrpc_strfree(cookieResponse); } }
static void addAuthorizationHeader(xmlrpc_env * const envP, struct curl_slist ** const headerListP, const char * const hdrValue) { const char * authorizationHeader; xmlrpc_asprintf(&authorizationHeader, "Authorization: %s", hdrValue); if (authorizationHeader == xmlrpc_strsol) xmlrpc_faultf(envP, "Couldn't allocate memory for " "Authorization header"); else { addHeader(envP, headerListP, authorizationHeader); xmlrpc_strfree(authorizationHeader); } }
void DateToLogString(time_t const datetime, const char **const dateStringP) { const char *tzo; struct tm tm; xmlrpc_localtime(datetime, &tm); tzo = tzOffsetStr(tm, datetime); xmlrpc_asprintf(dateStringP, "%02d/%s/%04d:%02d:%02d:%02d %s", tm.tm_mday, _DateMonth[tm.tm_mon], 1900 + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec, tzo); xmlrpc_strfree(tzo); }
static void makeChannelInfo(struct abyss_openssl_chaninfo ** const channelInfoPP, SSL * const sslP, const char ** const errorP) { struct abyss_openssl_chaninfo * channelInfoP; MALLOCVAR(channelInfoP); if (channelInfoP == NULL) xmlrpc_asprintf(errorP, "Unable to allocate memory"); else { *channelInfoPP = channelInfoP; *errorP = NULL; } }
static void parseHttpHostPortPath(const char *const hostportpath, const char **const hostP, unsigned short *const portP, const char **const pathP, const char **const errorP) { const char *path; char *buffer; buffer = strdup(hostportpath); if (!buffer) xmlrpc_asprintf(errorP, "Couldn't get memory for host/port/path buffer"); else { char *const slashPos = strchr(buffer, '/'); char *hostport; if (slashPos) { path = xmlrpc_strdupsol(slashPos); /* Includes the initial slash */ *slashPos = '\0'; /* NUL termination for hostport */ } else path = strdup("*"); hostport = buffer; /* The following interprets the port field without taking into account any %HH encoding, as the RFC says may be there. We ignore that remote possibility out of laziness. */ parseHostPort(hostport, hostP, portP, errorP); if (*errorP) xmlrpc_strfree(path); else *pathP = path; free(buffer); } }
static void waitForConnection(struct socketUnix * const listenSocketP, bool * const interruptedP, const char ** const errorP) { /*---------------------------------------------------------------------------- Wait for the listening socket to have a connection ready to accept. We return before the requested condition holds if the process receives (and catches) a signal, but only if it receives that signal a certain time after we start running. (That means this behavior isn't useful for most purposes). We furthermore return before the requested condition holds if someone sends a byte through the listening socket's interrupt pipe (or has sent one previously since the most recent time the pipe was drained). Return *interruptedP == true if we return before there is a connection ready to accept. -----------------------------------------------------------------------------*/ struct pollfd pollfds[2]; int rc; pollfds[0].fd = listenSocketP->fd; pollfds[0].events = POLLIN; pollfds[1].fd = listenSocketP->interruptPipe.interrupteeFd; pollfds[1].events = POLLIN; rc = poll(pollfds, ARRAY_SIZE(pollfds), -1); if (rc < 0) { if (errno == EINTR) { *errorP = NULL; *interruptedP = TRUE; } else { xmlrpc_asprintf(errorP, "poll() failed, errno = %d (%s)", errno, strerror(errno)); *interruptedP = FALSE; /* quiet compiler warning */ } } else { *errorP = NULL; *interruptedP = !(pollfds[0].revents & POLLIN); } }
void xmlrpc_read_datetime_8601(xmlrpc_env *const envP, const xmlrpc_value *const valueP, const char **const iso8601ValueP) { /*---------------------------------------------------------------------------- Get the datetime in ISO 8601 format. ISO 8601 allows a variety of representations for each datetime. The particular one we return is as in the following example. 19930214T131030,250000Z (13:10:30.25 on February 14, 1993) There are always 4 digits for the year. There are always 6 digits after the comma (microseconds). Midnight is hour 0, not 24. -----------------------------------------------------------------------------*/ validateDatetimeType(envP, valueP); if (!envP->fault_occurred) { xmlrpc_datetime dt; xmlrpc_read_datetime(envP, valueP, &dt); if (!envP->fault_occurred) { if (dt.Y > 9999) xmlrpc_faultf(envP, "Too far in future (year %u). " "ISO 8601 cannot " "represent years after AD 9999", dt.Y); else { xmlrpc_asprintf(iso8601ValueP, "%04u%02u%02uT%02u%02u%02u,%06uZ", dt.Y, dt.M, dt.D, dt.h, dt.m, dt.s, dt.u); if (xmlrpc_strnomem(*iso8601ValueP)) xmlrpc_faultf(envP, "Unable to allocate memory " "for datetime string"); if (envP->fault_occurred) xmlrpc_strfree(*iso8601ValueP); } } } }
static void addConnectionHeaderFld(TSession * const sessionP) { struct _TServer * const srvP = ConnServer(sessionP->connP)->srvP; if (HTTPKeepalive(sessionP)) { const char * keepaliveValue; ResponseAddField(sessionP, "Connection", "Keep-Alive"); xmlrpc_asprintf(&keepaliveValue, "timeout=%u, max=%u", srvP->keepalivetimeout, srvP->keepalivemaxconn); ResponseAddField(sessionP, "Keep-Alive", keepaliveValue); xmlrpc_strfree(keepaliveValue); } else ResponseAddField(sessionP, "Connection", "close"); }
static void initInterruptPipe(interruptPipe * const pipeP, const char ** const errorP) { int pipeFd[2]; int rc; rc = pipe(pipeFd); if (rc != 0) xmlrpc_asprintf(errorP, "Unable to create a pipe to use to interrupt " "waits. pipe() failed with errno %d (%s)", errno, strerror(errno)); else { *errorP = NULL; pipeP->interruptorFd = pipeFd[1]; pipeP->interrupteeFd = pipeFd[0]; } }
void DateToString(time_t const datetime, const char **const dateStringP) { struct tm brokenTime; xmlrpc_gmtime(datetime, &brokenTime); if (mktime(&brokenTime) == (time_t) -1) *dateStringP = NULL; else xmlrpc_asprintf(dateStringP, "%s, %02u %s %04u %02u:%02u:%02u UTC", _DateDay[brokenTime.tm_wday], brokenTime.tm_mday, _DateMonth[brokenTime.tm_mon], 1900 + brokenTime.tm_year, brokenTime.tm_hour, brokenTime.tm_min, brokenTime.tm_sec); }
static void makeChannelInfo(struct abyss_unix_chaninfo ** const channelInfoPP, struct sockaddr const peerAddr, socklen_t const peerAddrLen, const char ** const errorP) { struct abyss_unix_chaninfo * channelInfoP; MALLOCVAR(channelInfoP); if (channelInfoP == NULL) xmlrpc_asprintf(errorP, "Unable to allocate memory"); else { channelInfoP->peerAddrLen = peerAddrLen; channelInfoP->peerAddr = peerAddr; *errorP = NULL; } *channelInfoPP = channelInfoP; }
static void bindSocketToPortInet6(int const fd, uint16_t const portNumber, const char ** const errorP) { struct sockaddr_in6 name; int rc; name.sin6_family = AF_INET6; name.sin6_port = htons(portNumber); name.sin6_addr = in6addr_any; rc = bind(fd, (struct sockaddr *)&name, sizeof(name)); if (rc == -1) xmlrpc_asprintf(errorP, "Unable to bind IPv6 socket " "to port number %hu. " "bind() failed with errno %d (%s)", portNumber, errno, strerror(errno)); else *errorP = NULL; }
static void bindSocketToPort(int const fd, const struct sockaddr * const sockAddrP, socklen_t const sockAddrLen, const char ** const errorP) { int rc; rc = bind(fd, sockAddrP, sockAddrLen); if (rc == -1) xmlrpc_asprintf(errorP, "Unable to bind socket " "to the socket address. " "bind() failed with errno %d (%s)", errno, strerror(errno)); else { *errorP = NULL; if (SwitchTraceIsActive) traceSocketBound(sockAddrP, sockAddrLen); } }
static void createChanSwitch(struct _TServer * const srvP, const char ** const errorP) { TChanSwitch * chanSwitchP; const char * error; /* Not valid to call this when channel switch already exists: */ assert(srvP->chanSwitchP == NULL); createSwitchFromPortNum(srvP->port, &chanSwitchP, &error); if (error) { xmlrpc_asprintf(errorP, "Can't create channel switch. %s", error); xmlrpc_strfree(error); } else { *errorP = NULL; srvP->weCreatedChanSwitch = TRUE; srvP->chanSwitchP = chanSwitchP; } }
void ResponseError2(TSession * const sessionP, const char * const explanation) { const char * errorDocument; ResponseAddField(sessionP, "Content-type", "text/html"); ResponseWriteStart(sessionP); xmlrpc_asprintf(&errorDocument, "<HTML><HEAD><TITLE>Error %d</TITLE></HEAD>" "<BODY>" "<H1>Error %d</H1>" "<P>%s</P>" SERVER_HTML_INFO "</BODY>" "</HTML>", sessionP->status, sessionP->status, explanation); ConnWrite(sessionP->connP, errorDocument, strlen(errorDocument)); xmlrpc_strfree(errorDocument); }
static void chanSwitchListen(TChanSwitch * const chanSwitchP, uint32_t const backlog, const char ** const errorP) { struct socketUnix * const socketUnixP = chanSwitchP->implP; int32_t const minus1 = -1; int rc; /* Disable the Nagle algorithm to make persistant connections faster */ setsockopt(socketUnixP->fd, IPPROTO_TCP, TCP_NODELAY, &minus1, sizeof(minus1)); rc = listen(socketUnixP->fd, backlog); if (rc == -1) xmlrpc_asprintf(errorP, "listen() failed with errno %d (%s)", errno, strerror(errno)); else *errorP = NULL; }
static void addUserAgentHeader(xmlrpc_env * const envP, struct curl_slist ** const headerListP, const char * const userAgent) { if (userAgent) { /* Note: Curl has a CURLOPT_USERAGENT option that does some of this work. We prefer to be totally in control, though, so we build the header explicitly. */ curl_version_info_data * const curlInfoP = curl_version_info(CURLVERSION_NOW); char curlVersion[32]; const char * userAgentHeader; snprintf(curlVersion, sizeof(curlVersion), "%u.%u.%u", (curlInfoP->version_num >> 16) && 0xff, (curlInfoP->version_num >> 8) && 0xff, (curlInfoP->version_num >> 0) && 0xff ); xmlrpc_asprintf(&userAgentHeader, "User-Agent: %s Xmlrpc-c/%s Curl/%s", userAgent, XMLRPC_C_VERSION, curlVersion); if (userAgentHeader == xmlrpc_strsol) xmlrpc_faultf(envP, "Couldn't allocate memory for " "User-Agent header"); else { addHeader(envP, headerListP, userAgentHeader); xmlrpc_strfree(userAgentHeader); } } }
static void parseRequestUri(char *const requestUri, const char **const hostP, unsigned short *const portP, const char **const pathP, const char **const queryP, const char **const errorP) { /*---------------------------------------------------------------------------- Parse the request URI (in the request line "GET http://www.myserver.com:8080/myfile.cgi?parm HTTP/1.1", "http://www.myserver.com:8080/myfile.cgi?parm" is the request URI). Return as *hostP the "www.myserver.com" in the above example. If that part of the URI doesn't exist, return *hostP == NULL. Return as *portP the 8080 in the above example. If it doesn't exist, return 80. Return as *pathP the "/myfile.cgi" in the above example. If it doesn't exist, return "*". Return as *queryP the "parm" in the above example. If it doesn't exist, return *queryP == NULL. Return strings in newly malloc'ed storage. We can return syntactically invalid entities, e.g. a host name that contains "<", if 'requestUri' is similarly invalid. We should fix that some day. RFC 2396 lists a lot of characters as reserved for certain use in the URI, such as colon, and totally disallowed, such as space. -----------------------------------------------------------------------------*/ const char *requestUriNoQuery; /* The request URI with any query (the stuff marked by a question mark at the end of a request URI) chopped off. */ const char *query; const char *path; const char *host; unsigned short port; splitUriQuery(requestUri, &query, &requestUriNoQuery, errorP); if (!*errorP) { if (requestUriNoQuery[0] == '/') { host = NULL; path = xmlrpc_strdupsol(requestUriNoQuery); port = 80; *errorP = NULL; } else { if (!xmlrpc_strneq(requestUriNoQuery, "http://", 7)) xmlrpc_asprintf(errorP, "Scheme is not http://"); else parseHttpHostPortPath(&requestUriNoQuery[7], &host, &port, &path, errorP); } if (!*errorP) { *portP = port; unescapeHostPathQuery(host, path, query, hostP, pathP, queryP, errorP); if (host) xmlrpc_strfree(host); if (path) xmlrpc_strfree(path); } if (query) xmlrpc_strfree(query); xmlrpc_strfree(requestUriNoQuery); } }
void ConnCreate(TConn ** const connectionPP, TServer * const serverP, TChannel * const channelP, void * const channelInfoP, TThreadProc * const job, size_t const jobStackSize, TThreadDoneFn * const done, enum abyss_foreback const foregroundBackground, bool const useSigchld, const char ** const errorP) { /*---------------------------------------------------------------------------- Create an HTTP connection. A connection carries one or more HTTP transactions (request/response). *channelP transports the requests and responses. The connection handles those HTTP requests. The connection handles the requests primarily by running the function 'job' once. Some connections can do that autonomously, as soon as the connection is created. Others don't until Caller subsequently calls ConnProcess. Some connections complete the processing before ConnProcess return, while others may run the connection asynchronously to the creator, in the background, via a TThread thread. 'foregroundBackground' determines which. 'job' calls methods of the connection to get requests and send responses. Some time after the HTTP transactions are all done, 'done' gets called in some context. 'channelInfoP' == NULL means no channel info supplied. -----------------------------------------------------------------------------*/ TConn * connectionP; MALLOCVAR(connectionP); if (connectionP == NULL) xmlrpc_asprintf(errorP, "Unable to allocate memory for a connection " "descriptor."); else { connectionP->server = serverP; connectionP->channelP = channelP; connectionP->channelInfoP = channelInfoP; connectionP->buffer.b[0] = '\0'; connectionP->buffersize = 0; connectionP->bufferpos = 0; connectionP->finished = FALSE; connectionP->job = job; connectionP->done = done; connectionP->inbytes = 0; connectionP->outbytes = 0; connectionP->trace = getenv("ABYSS_TRACE_CONN"); makeThread(connectionP, foregroundBackground, useSigchld, jobStackSize, errorP); } *connectionPP = connectionP; }
abyss_bool RequestAuth(TSession * const sessionP, const char * const credential, const char * const user, const char * const pass) { /*---------------------------------------------------------------------------- Authenticate requester, in a very simplistic fashion. If the request executing on session *sessionP specifies basic authentication (via Authorization header) with username 'user', password 'pass', then return true. Else, return false and set up an authorization failure response (HTTP response status 401) that says user must supply an identity in the 'credential' domain. When we return true, we also set the username in the request info for the session to 'user' so that a future SessionGetRequestInfo can get it. -----------------------------------------------------------------------------*/ bool authorized; const char * authValue; authValue = RequestHeaderValue(sessionP, "authorization"); if (authValue) { char * const valueBuffer = malloc(strlen(authValue)); /* A buffer we can mangle as we parse the authorization: value */ if (!authValue) /* Should return error, but we have no way to do that */ authorized = false; else { const char * authType; char * authHdrPtr; strcpy(valueBuffer, authValue); authHdrPtr = &valueBuffer[0]; NextToken((const char **)&authHdrPtr); GetTokenConst(&authHdrPtr, &authType); if (authType) { if (xmlrpc_strcaseeq(authType, "basic")) { const char * userPass; char userPassEncoded[80]; NextToken((const char **)&authHdrPtr); xmlrpc_asprintf(&userPass, "%s:%s", user, pass); xmlrpc_base64Encode(userPass, userPassEncoded); xmlrpc_strfree(userPass); if (xmlrpc_streq(authHdrPtr, userPassEncoded)) { sessionP->requestInfo.user = xmlrpc_strdupsol(user); authorized = true; } else authorized = false; } else authorized = false; } else authorized = false; free(valueBuffer); } } else authorized = false; if (!authorized) { const char * hdrValue; xmlrpc_asprintf(&hdrValue, "Basic realm=\"%s\"", credential); ResponseAddField(sessionP, "WWW-Authenticate", hdrValue); xmlrpc_strfree(hdrValue); ResponseStatus(sessionP, 401); } return authorized; }
static void chanSwitchAccept(TChanSwitch * const chanSwitchP, TChannel ** const channelPP, void ** const channelInfoPP, const char ** const errorP) { /*---------------------------------------------------------------------------- Accept a connection via the channel switch *chanSwitchP. Return as *channelPP the channel for the accepted connection. If no connection is waiting at *chanSwitchP, wait until one is. If we receive a signal while waiting, return immediately with *channelPP == NULL. -----------------------------------------------------------------------------*/ struct socketWin * const listenSocketP = chanSwitchP->implP; HANDLE acceptEvent = WSACreateEvent(); bool interrupted; TChannel * channelP; interrupted = FALSE; /* Haven't been interrupted yet */ channelP = NULL; /* No connection yet */ *errorP = NULL; /* No error yet */ WSAEventSelect(listenSocketP->winsock, acceptEvent, FD_ACCEPT | FD_CLOSE | FD_READ); while (!channelP && !*errorP && !interrupted) { HANDLE interrupts[2] = {acceptEvent, listenSocketP->interruptEvent}; int rc; struct sockaddr peerAddr; socklen_t size = sizeof(peerAddr); rc = WaitForMultipleObjects(2, interrupts, FALSE, INFINITE); if (WAIT_OBJECT_0 + 1 == rc) { interrupted = TRUE; continue; }; rc = accept(listenSocketP->winsock, &peerAddr, &size); if (rc >= 0) { int const acceptedWinsock = rc; createChannelForAccept(acceptedWinsock, peerAddr, &channelP, channelInfoPP, errorP); if (*errorP) closesocket(acceptedWinsock); } else { int const lastError = WSAGetLastError(); if (lastError == WSAEINTR) interrupted = TRUE; else xmlrpc_asprintf(errorP, "accept() failed, WSA error = %d (%s)", lastError, getWSAError(lastError)); } } *channelPP = channelP; CloseHandle(acceptEvent); }
void ConnRead(TConn * const connectionP, uint32_t const timeout, bool * const eofP, bool * const timedOutP, const char ** const errorP) { /*---------------------------------------------------------------------------- Read some stuff on connection *connectionP from the channel. Read it into the connection's buffer. Don't wait more than 'timeout' seconds for data to arrive. If no data has arrived by then and 'timedOutP' is null, fail. If 'timedOut' is non-null, return as *timedOutP whether 'timeout' seconds passed without any data arriving. Also, stop waiting upon any interruption and treat it the same as a timeout. An interruption is either a signal received (and caught) at an appropriate time or a ChannelInterrupt() call before or during the wait. If 'eofP' is non-null, return *eofP == true, without reading anything, iff there will no more data forthcoming on the connection because client has closed the connection. If 'eofP' is null, fail in that case. -----------------------------------------------------------------------------*/ uint32_t const timeoutMs = timeout * 1000; if (timeoutMs < timeout) /* Arithmetic overflow */ xmlrpc_asprintf(errorP, "Timeout value is too large"); else { bool const waitForRead = TRUE; bool const waitForWrite = FALSE; bool readyForRead; bool failed; ChannelWait(connectionP->channelP, waitForRead, waitForWrite, timeoutMs, &readyForRead, NULL, &failed); if (failed) xmlrpc_asprintf(errorP, "Wait for stuff to arrive from client failed."); else { bool eof; if (readyForRead) { readFromChannel(connectionP, &eof, errorP); } else { /* Wait was interrupted, either by our requested timeout, a (caught) signal, or a ChannelInterrupt(). */ traceReadTimeout(connectionP, timeout); *errorP = NULL; eof = FALSE; } if (!*errorP) dealWithReadTimeout(timedOutP, !readyForRead, timeout, errorP); if (!*errorP) dealWithReadEof(eofP, eof, errorP); } } }
void ConnCreate(TConn ** const connectionPP, TServer * const serverP, TSocket * const connectedSocketP, TThreadProc * const job, TThreadDoneFn * const done, enum abyss_foreback const foregroundBackground, abyss_bool const useSigchld, const char ** const errorP) { /*---------------------------------------------------------------------------- Create an HTTP connection. A connection carries one or more HTTP transactions (request/response). 'connectedSocketP' transports the requests and responses. The connection handles those HTTP requests. The connection handles the requests primarily by running the function 'job' once. Some connections can do that autonomously, as soon as the connection is created. Others don't until Caller subsequently calls ConnProcess. Some connections complete the processing before ConnProcess return, while others may run the connection asynchronously to the creator, in the background, via a TThread thread. 'foregroundBackground' determines which. 'job' calls methods of the connection to get requests and send responses. Some time after the HTTP transactions are all done, 'done' gets called in some context. -----------------------------------------------------------------------------*/ TConn * connectionP; MALLOCVAR(connectionP); if (connectionP == NULL) xmlrpc_asprintf(errorP, "Unable to allocate memory for a connection " "descriptor."); else { abyss_bool success; uint16_t peerPortNumber; connectionP->server = serverP; connectionP->socketP = connectedSocketP; connectionP->buffersize = 0; connectionP->bufferpos = 0; connectionP->finished = FALSE; connectionP->job = job; connectionP->done = done; connectionP->inbytes = 0; connectionP->outbytes = 0; connectionP->trace = getenv("ABYSS_TRACE_CONN"); connectionP->start = time(NULL); SocketGetPeerName(connectedSocketP, &connectionP->peerip, &peerPortNumber, &success); if (success) makeThread(connectionP, foregroundBackground, useSigchld, errorP); else xmlrpc_asprintf(errorP, "Failed to get peer name from socket."); } *connectionPP = connectionP; }