/* **============================================================================== ** ** Open Management Infrastructure (OMI) ** ** Copyright (c) Microsoft Corporation ** ** Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 ** ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, ** MERCHANTABLITY OR NON-INFRINGEMENT. ** ** See the Apache 2 License for the specific language governing permissions ** and limitations under the License. ** **============================================================================== */ #include #include #include "http.h" #include "addr.h" #include "sock.h" #include "selector.h" #include #include #include #include #include #include #include #ifdef CONFIG_POSIX #include #include #else /* ssl not supported in this configuration; just make compiler happy */ typedef void SSL; typedef void SSL_CTX; #define SSL_CTX_free(c) #define SSL_new(c) 0 #define SSL_free(c) #define SSL_set_fd(c,a) (a==a) #define SSL_read(c,a,b) 0 #define SSL_write(c,a,b) 0 #define SSL_get_error(c,e) e #define SSL_ERROR_WANT_WRITE 0 #define SSL_ERROR_WANT_READ 1 #define SSL_ERROR_SYSCALL 2 #ifdef EWOULDBLOCK # undef EWOULDBLOCK #endif #define EWOULDBLOCK 0 #ifdef EINPROGRESS # undef EINPROGRESS #endif #define EINPROGRESS 0 #define ERR_get_error() 0 #define ERR_error_string_n(c,a,b) a[0]=0 #define SSL_accept(c) 0 #endif #define T MI_T /* #define ENABLE_TRACING // */ #ifdef ENABLE_TRACING #define PRINTF(a) TIMESTAMP(); printf a #define TIMESTAMP() \ {\ MI_Uint64 currentTimeUsec = 0;\ \ Time_Now(¤tTimeUsec);\ currentTimeUsec /= 1000; /* ms */ \ printf("%ds%03dms ", (int)(currentTimeUsec / 1000 % 1000), (int)(currentTimeUsec % 1000));\ } #define PRINTF_2(a) #else #define PRINTF(a) #define PRINTF_2(a) #endif /* **============================================================================== ** ** Local definitions: ** **============================================================================== */ static const MI_Uint32 _MAGIC = 0xE0BB5FD3; static const MI_Uint32 MAX_HEADER_SIZE = 2 * 1024; static const MI_Uint32 INITIAL_BUFFER_SIZE = 2 * 1024; static const size_t HTTP_MAX_CONTENT = 1024 * 1024; struct _Http { MI_Uint32 magic; Selector internalSelector; Selector* selector; HttpCallbackOnNewConnection callbackOnNewConnection; HttpCallbackOnCloseConnection callbackOnCloseConnection; HttpCallbackOnRequest callbackOnRequest; void* callbackData; SSL_CTX* sslContext; /* options: timeouts etc */ HttpOptions options; MI_Boolean internalSelectorUsed; }; typedef struct _Http_Listener_SocketData { /* based member*/ Handler base; MI_Boolean secure; } Http_Listener_SocketData; typedef enum _Http_RecvState { RECV_STATE_HEADER, RECV_STATE_CONTENT } Http_RecvState; typedef struct _Http_SR_SocketData { /* based member*/ Handler base; /* ssl part */ SSL* ssl; MI_Boolean reverseOperations; /*reverse read/write Events/Handlers*/ MI_Boolean acceptDone; /* is server/provider is processing request (to disbale timeout) */ MI_Boolean requestIsBeingProcessed; /* link to next stack layer */ void* connectionData; /* receiving data */ char* recvBuffer; size_t recvBufferSize; size_t recvievedSize; Http_RecvState recvingState; HttpHeaders recvHeaders; Page* recvPage; /* sending part */ Page* sendPage; size_t sentSize; Http_RecvState sendingState; int httpErrorCode; } Http_SR_SocketData; /* helper functions result */ typedef enum _Http_CallbackResult { PRT_CONTINUE, PRT_RETURN_TRUE, PRT_RETURN_FALSE } Http_CallbackResult; MI_INLINE MI_Uint8 _ToLower(MI_Uint8 x) { return (MI_Uint8)tolower(x); } #define _HashCode(first,last,len) ( (((MI_Uint8)first) << 16) | (((MI_Uint8)last) << 8) | (((MI_Uint16)len)) ) static MI_Boolean _DecodeBasicAuth( HttpHeaders* recvHeaders, const char * src, char* tgt) { /* decoding array 0-63 - valid value, 64-padding, 65-skip, -1-invalid */ static const char s_decodingArray[] = { /* 0 - 31 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 32 - 39 !"#$%&' */ -1, -1, -1, -1, -1, -1, -1, -1, /* ()*+,-./ */ -1, -1, -1, 62, -1, -1, -1, 63, /* 0 -9 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* :;<=>? */ -1,-1,-1,64,-1,-1, /* @ A-Z */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, /* []\\^_` */ -1,-1,-1,-1,-1,-1, /* a-z */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, /* {|} ~ 127 */ -1, -1, -1, -1, -1 }; char* startBuffer = tgt; /* skip spaces in value */ while (src[0] == ' ' || src[0] == '\t') src++; /* perform decoding - 4 input bytes are converted into 3 output, using porvided pointers src/tgt since decoding is done inplace and result is smaller than input, no checks for buffer size is required */ while (*src) { signed char b1, b2, b3, b4; b1 = s_decodingArray[(*src++) & 0x7f]; if (b1 < 0 ) return MI_FALSE; b2 = s_decodingArray[(*src++) & 0x7f]; if (b2 < 0 ) return MI_FALSE; b3 = s_decodingArray[(*src++) & 0x7f]; if (b3 < 0 ) return MI_FALSE; b4 = s_decodingArray[(*src++) & 0x7f]; if (b4 < 0 ) return MI_FALSE; *tgt++ = (b1 << 2) | (b2 >> 4); if (b3 != 64) *tgt++ = ((b2&0xF) << 4) | (b3 >> 2); if (b4 != 64) *tgt++ = ((b3&0x3) << 6) | (b4); } *tgt = 0; /* decoded string has format: [:] */ recvHeaders->username = startBuffer; startBuffer = strchr(startBuffer, ':'); if ( startBuffer ) { *startBuffer = 0; recvHeaders->password = startBuffer + 1; } return MI_TRUE; } static MI_Boolean _getNameValuePair( char ** line, char ** value, int* nameHashCode ) { int len = 0; char* p; /* find name end /hash-code */ *nameHashCode = _ToLower((MI_Uint8)(*line)[0])<<16; for (len = 1; (*line)[len] != ':' && (*line)[len] != '\r'; len++ ) ; if ((*line)[len] != ':') return MI_FALSE; *nameHashCode |= (len) | _ToLower((MI_Uint8)(*line)[len-1])<<8; (*line)[len] = 0; p = *line + len + 1; /* skip spaces in value */ while (p[0] == ' ' || p[0] == '\t') p++; *value = p; /* skip to end of line */ for ( ; ; ) { if (p[0] == '\r' && p[1] == '\n' && (p[2] != ' ' && p[2] != '\t') ) { p[0] = 0; (*line) = p + 2; break; } p ++; } /* remove trailing spaces */ p--; while (p[0] == ' ' || p[0] == '\t') p--; p[1] = 0; return MI_TRUE; } static void _ParseContentType( HttpHeaders* recvHeaders, char* value) { recvHeaders->contentType = value; /* find attribute list */ while (value[0] != 0 && value[0] != ';') value++; /* Check if attribute list was provided */ if (value[0] == 0) return; /* terminate type/subtype; move to attributes list */ value[0] = 0; value++; /* skip spaces in value */ while (value[0] == ' ' || value[0] == '\t') value++; /* find charset attribute (if present) */ if (Strncasecmp(value,"charset=",8) != 0) return; value += 8; recvHeaders->charset = value; /* can be enclosed in quotes */ if (value[0] == '"') { /* skip '"' */ recvHeaders->charset++; value++; value = strchr(value, '"'); if (value) *value = 0; else recvHeaders->charset = 0; } else { /*skip trialing spaces */ while (value[0] != 0 && value[0] != ' ' && value[0] != '\t') value++; *value = 0; } } static MI_Boolean _getHeaderField( Http_SR_SocketData* handler, char ** line) { char* name = *line; char* value = NULL; int nameHashCode; if (!_getNameValuePair(line, &value, &nameHashCode)) return MI_FALSE; switch (nameHashCode) { case (_HashCode('c','e',12)): /*Content-Type*/ if (Strcasecmp(name,"Content-Type") == 0) _ParseContentType(&handler->recvHeaders, value); break; case (_HashCode('c','h',14)): /*Content-Length*/ if (Strcasecmp(name,"Content-Length") == 0) { handler->recvHeaders.contentLength = (size_t)Strtoull(value, NULL, 10); if ( handler->recvHeaders.contentLength > HTTP_MAX_CONTENT ) return MI_FALSE; } break; case (_HashCode('a','n',13)): /*Authorization*/ if (Strcasecmp(name,"Authorization") == 0) { if (Strncasecmp(value,"Basic",5) == 0) { /* since decoded string is smaller, performing decoding inplace; for source skip 'Basic ' part (6 chars) */ if (!_DecodeBasicAuth(&handler->recvHeaders, value + 6, value)) { LOGW_CHAR(("base64 decoding error in Basic auth: [%s]\n", value)); return MI_FALSE; } } else /* unknown authorization type */ handler->recvHeaders.authorization = value; } break; default: break; } return MI_TRUE; } static MI_Boolean _getRequestLine( Http_SR_SocketData* handler, char ** line) { size_t index; /* expecting Request-Line = Method SP Request-URI SP HTTP-Version CRLF Read more: http://www.faqs.org/rfcs/rfc2616.html#ixzz0jKdjJdZv */ /* skip to end of line */ for ( index = 1; index < handler->recvievedSize; index++ ) { if ((*line)[index-1] == '\r' && (*line)[index] == '\n' ) { (*line) = (*line) + index + 1; return MI_TRUE; } } return MI_FALSE; } static MI_Result _Sock_Read( Http_SR_SocketData* handler, void* buf, size_t buf_size, size_t* sizeRead) { int res; if (!handler->ssl) return Sock_Read(handler->base.sock, buf, buf_size, sizeRead); handler->base.mask &= ~SELECTOR_WRITE; handler->base.mask |= SELECTOR_READ; handler->reverseOperations = MI_FALSE; *sizeRead = 0; if (handler->acceptDone) { res = SSL_read(handler->ssl, buf, buf_size); PRINTF(("ssl read %d\n", res)); } else { res = SSL_accept(handler->ssl); PRINTF(("ssl accept %d\n", res)); if ( res > 0 ) { /* we are done with accpet */ handler->acceptDone = MI_TRUE; return _Sock_Read(handler,buf,buf_size,sizeRead); } /* perform regular error checking */ } if ( res == 0 ) return MI_RESULT_OK; /* connection closed */ if ( res > 0 ) { *sizeRead = res; return MI_RESULT_OK; /* ok */ } switch (SSL_get_error(handler->ssl, res)) { case SSL_ERROR_WANT_WRITE: handler->reverseOperations = MI_TRUE; /* wait until write is allowed */ handler->base.mask |= SELECTOR_WRITE; handler->base.mask &= ~SELECTOR_READ; PRINTF(("ssl read/accept WANT_WRITE\n")); return MI_RESULT_WOULD_BLOCK; case SSL_ERROR_WANT_READ: PRINTF(("ssl read/accept WANT_READ\n")); return MI_RESULT_WOULD_BLOCK; case SSL_ERROR_SYSCALL: if (EAGAIN == errno || EWOULDBLOCK == errno || EINPROGRESS == errno) return MI_RESULT_WOULD_BLOCK; LOGW_CHAR(("ssl-read: unexpected sys error %d\n", errno)); break; default: { /* print error */ unsigned long err = ERR_get_error(); while (err) { char err_txt[200]; ERR_error_string_n(err, err_txt, sizeof(err_txt)); LOGW_CHAR(("ssl-read error: %d [%s]\n", (int)err, err_txt)); err = ERR_get_error(); } } break; } return MI_RESULT_FAILED; } static MI_Result _Sock_Write( Http_SR_SocketData* handler, void* buf, size_t buf_size, size_t* sizeWritten) { int res; if (!handler->ssl) return Sock_Write(handler->base.sock, buf, buf_size, sizeWritten); /* Do not clear READ flag, since 'close' notification delivered as READ event*/ handler->base.mask &= ~SELECTOR_READ; handler->base.mask |= SELECTOR_WRITE; handler->reverseOperations = MI_FALSE; *sizeWritten = 0; res = SSL_write(handler->ssl, buf, buf_size); PRINTF(("ssl write %d\n", res)); if ( res == 0 ) return MI_RESULT_OK; /* connection closed */ if ( res > 0 ) { *sizeWritten = res; return MI_RESULT_OK; /* ok */ } switch (SSL_get_error(handler->ssl, res)) { case SSL_ERROR_WANT_WRITE: return MI_RESULT_WOULD_BLOCK; case SSL_ERROR_WANT_READ: handler->reverseOperations = MI_TRUE; /* wait until write is allowed */ handler->base.mask |= SELECTOR_READ; handler->base.mask &= ~SELECTOR_WRITE; return MI_RESULT_WOULD_BLOCK; case SSL_ERROR_SYSCALL: if (EAGAIN == errno || EWOULDBLOCK == errno || EINPROGRESS == errno) return MI_RESULT_WOULD_BLOCK; LOGW_CHAR(("ssl-write: unexpected sys error %d\n", errno)); break; default: break; } return MI_RESULT_FAILED; } static Http_CallbackResult _ReadHeader( Http_SR_SocketData* handler) { char* buf; char* currentLine; char* data; size_t buf_size, received, index; MI_Result r; MI_Boolean fullHeaderReceived = MI_FALSE; /* are we done with header? */ if (handler->recvingState == RECV_STATE_CONTENT) return PRT_CONTINUE; buf = handler->recvBuffer + handler->recvievedSize; buf_size = handler->recvBufferSize - handler->recvievedSize; received = 0; r = _Sock_Read(handler, buf, buf_size, &received); PRINTF(("%d: read r = %d, recv = %d; reverse %d\n", (int)handler->base.sock, (int)r, (int)received, (int)handler->reverseOperations )); if ( r == MI_RESULT_OK && 0 == received ) return PRT_RETURN_FALSE; /* conection closed */ if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) return PRT_RETURN_FALSE; if (!received) return PRT_RETURN_TRUE; handler->recvievedSize += received; /* check header */ PRINTF_2(("%s\n",buf)); /* did we get full header? */ buf = handler->recvBuffer; for ( index = 3; index < handler->recvievedSize; index++ ) { if (buf[index-3] == '\r' && buf[index-1] == '\r' && buf[index-2] == '\n' && buf[index] == '\n' ) { fullHeaderReceived = MI_TRUE; break; } } if (!fullHeaderReceived ) { if ( handler->recvievedSize < handler->recvBufferSize ) return PRT_RETURN_TRUE; /* continue reading */ if ( handler->recvBufferSize < MAX_HEADER_SIZE ) { buf = realloc(handler->recvBuffer, handler->recvBufferSize * 2); if (!buf) return PRT_RETURN_FALSE; handler->recvBufferSize *= 2; handler->recvBuffer = buf; return _ReadHeader(handler); } else { /* http header is too big - drop connection */ LOGW((T("http header is too big; dropping connection\n"))); return PRT_RETURN_FALSE; } } /* consume data */ currentLine = buf; data = buf + index + 1; /* pointer to data in case we got some */ if (!_getRequestLine(handler, ¤tLine)) return PRT_RETURN_FALSE; while ((data-currentLine) > 3) { if (!_getHeaderField(handler, ¤tLine)) return PRT_RETURN_FALSE; } /* Allocate zero-terminated buffer */ handler->recvPage = (Page*)malloc(sizeof(Page) + handler->recvHeaders.contentLength + 1); if (!handler->recvPage) return PRT_RETURN_FALSE; ((char*)(handler->recvPage + 1))[handler->recvHeaders.contentLength] = 0; handler->recvPage->u.s.size = (unsigned int)handler->recvHeaders.contentLength; handler->recvPage->u.s.next = 0; handler->recvievedSize -= index + 1; /* Verify that we have not more than 'content-length' bytes in buffer left If we hvae more, assuming http client is invalid and drop connection */ if (handler->recvievedSize > handler->recvHeaders.contentLength) { LOGW((T("http payload is bigger than content-length\n"))); return PRT_RETURN_FALSE; } memcpy( handler->recvPage + 1, data, handler->recvievedSize ); handler->recvingState = RECV_STATE_CONTENT; PRINTF_2(("full header read; page size %d, position %d\n", handler->recvPage->u.s.size, handler->recvievedSize)); return PRT_CONTINUE; } static Http_CallbackResult _ReadData( Http_SR_SocketData* handler) { Http* self = (Http*)handler->base.data; char* buf; size_t buf_size, received; MI_Result r; /* are we in the right state? */ if (handler->recvingState != RECV_STATE_CONTENT) return PRT_RETURN_FALSE; buf = ((char*)(handler->recvPage + 1)) + handler->recvievedSize; buf_size = handler->recvHeaders.contentLength - handler->recvievedSize; received = 0; if (buf_size) { r = _Sock_Read(handler, buf, buf_size, &received); PRINTF(("%d: read r = %d, recv = %d\n", (int)handler->base.sock, (int)r, (int)received )); if ( r == MI_RESULT_OK && 0 == received ) return PRT_RETURN_FALSE; /* conection closed */ if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) return PRT_RETURN_FALSE; handler->recvievedSize += received; } /* did we get all data? */ PRINTF_2(("dt status - %d / %d\n", (int)handler->recvievedSize, (int)handler->recvHeaders.contentLength)); if ( handler->recvievedSize != handler->recvHeaders.contentLength ) return PRT_RETURN_TRUE; handler->requestIsBeingProcessed = MI_TRUE; (*self->callbackOnRequest)( self, self->callbackData, handler->connectionData, handler, &handler->recvHeaders, &handler->recvPage ); if (handler->recvPage) free(handler->recvPage); handler->recvPage = 0; handler->recvievedSize = 0; memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders)); handler->recvingState = RECV_STATE_HEADER; return PRT_CONTINUE; } static MI_Boolean _RequestCallbackRead( Http_SR_SocketData* handler) { switch (_ReadHeader(handler)) { case PRT_CONTINUE: break; case PRT_RETURN_TRUE: return MI_TRUE; case PRT_RETURN_FALSE: return MI_FALSE; } switch (_ReadData(handler)) { case PRT_CONTINUE: break; case PRT_RETURN_TRUE: return MI_TRUE; case PRT_RETURN_FALSE: return MI_FALSE; } return MI_TRUE; } /* length of longest description - has to be updated if descriptions are updated */ #define HTTP_LONGEST_ERROR_DESCRIPTION 50 static const char* _GetHttpErrorCodeDescription( int httpErrorCode ) { switch (httpErrorCode) { case 200: return "OK"; case 400: return "Bad Request"; case 401: return "Unauthorized"; case 500: return "Internal Server Error"; } return "Error"; } static Http_CallbackResult _WriteHeader( Http_SR_SocketData* handler) { #define RESPONSE_HEADER_FMT \ "HTTP/1.1 %d %s\r\n" \ "Content-Length: %d\r\n"\ "Connection: Keep-Alive\r\n"\ "Content-Type: application/soap+xml;charset=UTF-8\r\n"\ "\r\n" /* "SOAPAction: http://schemas.xmlsoap.org/ws/2004/08/addressing/fault\r\n"\ */ char currentLine[sizeof(RESPONSE_HEADER_FMT) + 10 /* content length */ + 10 /*error code*/ + HTTP_LONGEST_ERROR_DESCRIPTION /* code descirpiton */ ]; char* buf; size_t buf_size, sent; MI_Result r; /* Do we have any data to send? */ if (!handler->sendPage && 0 == handler->httpErrorCode) return PRT_RETURN_TRUE; /* are we done with header? */ if (handler->sendingState == RECV_STATE_CONTENT) return PRT_CONTINUE; if (handler->sendPage) { buf_size = (size_t)Snprintf( currentLine, sizeof(currentLine), RESPONSE_HEADER_FMT, (int)handler->httpErrorCode, _GetHttpErrorCodeDescription(handler->httpErrorCode), (int)handler->sendPage->u.s.size ); } else { buf_size = (size_t)Snprintf( currentLine, sizeof(currentLine), "HTTP/1.1 %d %s\r\n\r\n", (int)handler->httpErrorCode, _GetHttpErrorCodeDescription(handler->httpErrorCode)); } buf = currentLine + handler->sentSize; sent = 0; r = _Sock_Write(handler, buf, buf_size - handler->sentSize, &sent); PRINTF(("%d: write r = %d, sent = %d; reverse %d\n", (int)handler->base.sock, (int)r, (int)sent, (int)handler->reverseOperations )); if ( r == MI_RESULT_OK && 0 == sent ) return PRT_RETURN_FALSE; /* conection closed */ if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) return PRT_RETURN_FALSE; if (!sent) return PRT_RETURN_TRUE; handler->sentSize += sent; if (handler->sentSize < buf_size) return PRT_RETURN_TRUE; handler->sentSize = 0; handler->sendingState = RECV_STATE_CONTENT; return PRT_CONTINUE; } static Http_CallbackResult _WriteData( Http_SR_SocketData* handler) { char* buf; size_t buf_size, sent; MI_Result r; /* are we in the right state? */ if (handler->sendingState != RECV_STATE_CONTENT) return PRT_RETURN_FALSE; if (!handler->sendPage) { /* no content*/ handler->httpErrorCode = 0; handler->sentSize = 0; handler->sendingState = RECV_STATE_HEADER; handler->base.mask &= ~SELECTOR_WRITE; handler->base.mask |= SELECTOR_READ; return PRT_CONTINUE; } buf = ((char*)(handler->sendPage + 1)) + handler->sentSize; buf_size = handler->sendPage->u.s.size - handler->sentSize; sent = 0; r = _Sock_Write(handler, buf, buf_size, &sent); PRINTF(("%d: write r = %d, sent = %d\n", (int)handler->base.sock, (int)r, (int)sent )); if ( r == MI_RESULT_OK && 0 == sent ) return PRT_RETURN_FALSE; /* conection closed */ if ( r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK ) return PRT_RETURN_FALSE; handler->sentSize += sent; /* did we get all data? */ if ( handler->sentSize != handler->sendPage->u.s.size ) return PRT_RETURN_TRUE; free(handler->sendPage); handler->sendPage = 0; handler->httpErrorCode = 0; handler->sentSize = 0; handler->sendingState = RECV_STATE_HEADER; handler->base.mask &= ~SELECTOR_WRITE; handler->base.mask |= SELECTOR_READ; return PRT_CONTINUE; } static MI_Boolean _RequestCallbackWrite( Http_SR_SocketData* handler) { switch (_WriteHeader(handler)) { case PRT_CONTINUE: break; case PRT_RETURN_TRUE: return MI_TRUE; case PRT_RETURN_FALSE: return MI_FALSE; } switch (_WriteData(handler)) { case PRT_CONTINUE: break; case PRT_RETURN_TRUE: return MI_TRUE; case PRT_RETURN_FALSE: return MI_FALSE; } return MI_TRUE; } static MI_Boolean _RequestCallback( Selector* sel, Handler* handlerIn, MI_Uint32 mask, MI_Uint64 currentTimeUsec) { Http_SR_SocketData* handler = (Http_SR_SocketData*)handlerIn; sel=sel; if ( ((mask & SELECTOR_READ) != 0 && !handler->reverseOperations) || ((mask & SELECTOR_WRITE) != 0 && handler->reverseOperations) ) { if (!_RequestCallbackRead(handler)) return MI_FALSE; } if ( ((mask & SELECTOR_WRITE) != 0 && !handler->reverseOperations) || ((mask & SELECTOR_READ) != 0 && handler->reverseOperations) ) { if (!_RequestCallbackWrite(handler)) return MI_FALSE; } /* re-set timeout - if we performed R/W operation, set timeout depending where we are in communication */ if (mask & (SELECTOR_READ | SELECTOR_WRITE)) { Http* self = (Http*)handler->base.data; if (handler->requestIsBeingProcessed) { /* since request is processed by server, disable timeout for this period */ handler->base.fireTimeoutAt = TIME_NEVER; } else { /* Use configuration timeout */ handler->base.fireTimeoutAt = currentTimeUsec + self->options.timeoutUsec; } } /* Close conenction by timeout */ if (mask & SELECTOR_TIMEOUT) return MI_FALSE; if ((mask & SELECTOR_REMOVE) != 0 || (mask & SELECTOR_DESTROY) != 0) { Http* self = (Http*)handler->base.data; /* notify next stack layer */ (*self->callbackOnCloseConnection)( self, self->callbackData, handler->connectionData ); if (handler->ssl) SSL_free(handler->ssl); PRINTF(("%d: close\n", (int)handler->base.sock)); Sock_Close(handler->base.sock); if (handler->recvPage) free(handler->recvPage); if (handler->sendPage) free(handler->sendPage); free(handler->recvBuffer); free(handler); } return MI_TRUE; } static MI_Boolean _ListenerCallback( Selector* sel, Handler* handler_, MI_Uint32 mask, MI_Uint64 currentTimeUsec) { Http_Listener_SocketData* handler = (Http_Listener_SocketData*)handler_; Http* self = (Http*)handler->base.data; MI_Result r; Sock s; Addr addr; Http_SR_SocketData* h; sel=sel; mask=mask; currentTimeUsec = currentTimeUsec; if (mask & SELECTOR_READ) { int count; for (count = 0; count < 5; count++) { /* Accept the incoming connection */ r = Sock_Accept(handler->base.sock, &s, &addr); PRINTF(("%d: accept r = %d\n", (int)s, (int)r )); if (MI_RESULT_WOULD_BLOCK == r) return MI_TRUE; if (r != MI_RESULT_OK) { LOGW((T("Sock_Accept() failed; err %d\n"), Sock_GetLastError())); return MI_TRUE; } r = Sock_SetBlocking(s, MI_FALSE); if (r != MI_RESULT_OK) { LOGW((T("Sock_SetBlocking() failed\n"))); Sock_Close(s); return MI_TRUE; } /* Create handler */ h = (Http_SR_SocketData*)calloc(1, sizeof(Http_SR_SocketData)); if (!h) { Sock_Close(s); return MI_TRUE; } h->recvBufferSize = INITIAL_BUFFER_SIZE; h->recvBuffer = (char*)calloc(1, h->recvBufferSize); if (!h->recvBuffer) { free(h); Sock_Close(s); return MI_TRUE; } h->base.sock = s; h->base.mask = SELECTOR_READ | SELECTOR_EXCEPTION; h->base.callback = _RequestCallback; h->base.data = self; h->base.fireTimeoutAt = currentTimeUsec + self->options.timeoutUsec; /* ssl support */ if (handler->secure) { h->ssl = SSL_new(self->sslContext); if (!h->ssl) { LOGW((T("ssl_new() failed\n"))); free(h); Sock_Close(s); return MI_TRUE; } if (!(SSL_set_fd(h->ssl, s) )) { LOGW((T("ssl_set_fd() failed\n"))); SSL_free(h->ssl); free(h); Sock_Close(s); return MI_TRUE; } } /* Watch for read events on the incoming connection */ r = Selector_AddHandler(self->selector, &h->base); if (r != MI_RESULT_OK) { LOGW((T("Selector_AddHandler() failed\n"))); if (handler->secure) SSL_free(h->ssl); free(h); Sock_Close(s); return MI_TRUE; } /* notify next stack layer about new connection */ (*self->callbackOnNewConnection)( self, self->callbackData, h, &h->connectionData ); } } if ((mask & SELECTOR_REMOVE) != 0 || (mask & SELECTOR_DESTROY) != 0) { Sock_Close(handler->base.sock); free(handler); } return MI_TRUE; } static MI_Result _New_Http( Http** selfOut, Selector* selector, /*optional, maybe NULL*/ HttpCallbackOnNewConnection callbackOnNewConnection, HttpCallbackOnCloseConnection callbackOnCloseConnection, HttpCallbackOnRequest callbackOnRequest, void* callbackData) { Http* self; /* Check parameters */ if (!selfOut) return MI_RESULT_INVALID_PARAMETER; /* Clear output parameter */ *selfOut = NULL; /* Allocate structure */ { self = (Http*)calloc(1, sizeof(Http)); if (!self) return MI_RESULT_FAILED; } if (selector) { /* attach the exisiting selector */ self->selector = selector; self->internalSelectorUsed = MI_FALSE; } else { /* creaet a new selector */ /* Initialize the network */ Sock_Start(); /* Initialize the selector */ if (Selector_Init(&self->internalSelector) != MI_RESULT_OK) { free(self); return MI_RESULT_FAILED; } self->selector = &self->internalSelector; self->internalSelectorUsed = MI_TRUE; } /* Save the callback and callbackData */ self->callbackOnRequest = callbackOnRequest; self->callbackOnCloseConnection = callbackOnCloseConnection; self->callbackOnNewConnection = callbackOnNewConnection; self->callbackData = callbackData; /* Set the magic number */ self->magic = _MAGIC; /* options */ { HttpOptions options = DEFAULT_HTTP_OPTIONS; self->options = options; } /* Set output parameter */ *selfOut = self; return MI_RESULT_OK; } #ifdef CONFIG_POSIX static MI_Boolean _verifyPrivateKey( SSL_CTX *ctx, const char* keyPath) { // Open the private key file. FILE* is = fopen(keyPath, "r"); if (!is) { LOGE_CHAR(( "---> SSL: failed to open private key file: %s", keyPath)); return MI_FALSE; } // Read the private key from the input stream. EVP_PKEY* pkey; pkey = PEM_read_PrivateKey(is, NULL, NULL, NULL); if (!pkey) { LOGE_CHAR(("---> SSL: failed to create private key")); return MI_FALSE; } /* Close the input stream. */ fclose(is); /* Associate the new private key with the SSL context object. */ if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0) { EVP_PKEY_free(pkey); LOGE_CHAR(( "---> SSL: no private key found in %s", keyPath)); return MI_FALSE; } EVP_PKEY_free(pkey); /* Check private key for validity. */ if (!SSL_CTX_check_private_key(ctx)) { LOGE_CHAR(( "---> SSL: Private and public key do not match")); return MI_FALSE; } return MI_TRUE; } static MI_Result _CreateSSLContext(Http* self) { SSL_CTX * sslContext = 0; sslContext = SSL_CTX_new(SSLv23_method()); if (!sslContext) { LOGE_CHAR(( "---> SSL: cannot create ssl context")); return MI_RESULT_FAILED; } SSL_CTX_set_quiet_shutdown(sslContext, 1); SSL_CTX_set_mode(sslContext, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF); /* Check if there is a certificate file (file containing server ** certificate) specified. If specified, validate and load the ** certificate. */ { /* load the specified server certificates */ LOGI_CHAR(("---> SSL: Loading server certificate from: %s", GetPath(ID_PEMFILE))); if (SSL_CTX_use_certificate_file(sslContext, GetPath(ID_PEMFILE), SSL_FILETYPE_PEM) <=0) { LOGE_CHAR(("---> SSL: No server certificate found in %s", GetPath(ID_PEMFILE))); SSL_CTX_free(sslContext); return MI_RESULT_FAILED; } } /* ** Check if there is a key file (file containing server ** private key) specified and the key was not already loaded. ** If specified, validate and load the key. */ { /* load the specified server certificates */ LOGI_CHAR(("---> SSL: Loading certificate's private key from: %s", GetPath(ID_KEYFILE))); // // load given private key and check for validity // if (!_verifyPrivateKey(sslContext, GetPath(ID_KEYFILE))) { LOGE_CHAR(( "---> SSL: No server certificate found in %s", GetPath(ID_KEYFILE))); SSL_CTX_free(sslContext); return MI_RESULT_FAILED; } } self->sslContext = sslContext; return MI_RESULT_OK; } #endif static MI_Result _CreateAddListenerSocket( Http* self, unsigned short port, MI_Boolean secure ) { Addr addr; Sock listener; MI_Result r; /* Create listener socket */ { Addr_InitAny(&addr, port); r = Sock_CreateListener(&listener, &addr); if (r != MI_RESULT_OK) { return r; } r = Sock_SetBlocking(listener, MI_FALSE); if (r != MI_RESULT_OK) { Sock_Close(listener); return r; } } /* Watch for read events on the listener socket (client connections) */ { Http_Listener_SocketData* h = (Http_Listener_SocketData*)calloc(1, sizeof(Http_Listener_SocketData)); if (!h) { Sock_Close(listener); return MI_RESULT_FAILED; } h->base.sock = listener; h->base.mask = SELECTOR_READ | SELECTOR_EXCEPTION; h->base.callback = _ListenerCallback; h->base.data = self; h->secure = secure; r = Selector_AddHandler(self->selector, &h->base); if (r != MI_RESULT_OK) { Sock_Close(listener); free(h); return r; } } return MI_RESULT_OK; } MI_Result Http_New_Server( Http** selfOut, Selector* selector, /*optional, maybe NULL*/ unsigned short http_port, /* 0 to disable */ unsigned short https_port, /* 0 to disable */ HttpCallbackOnNewConnection callbackOnNewConnection, HttpCallbackOnCloseConnection callbackOnCloseConnection, HttpCallbackOnRequest callbackOnRequest, void* callbackData) { Http* self; MI_Result r; /* allocate this, inits selector */ r = _New_Http(selfOut, selector, callbackOnNewConnection, callbackOnCloseConnection, callbackOnRequest, callbackData); if (MI_RESULT_OK != r) return r; self = *selfOut; /* Create http listener socket */ if (http_port) { r = _CreateAddListenerSocket(self, http_port, MI_FALSE); if (r != MI_RESULT_OK) { Http_Delete(self); return r; } } #ifdef CONFIG_POSIX /* Create https listener socket */ if (https_port) { /* init ssl */ SSL_library_init(); /* create context */ r = _CreateSSLContext(self); if (r != MI_RESULT_OK) { Http_Delete(self); return r; } /* create a socket */ r = _CreateAddListenerSocket(self, https_port, MI_TRUE); if (r != MI_RESULT_OK) { Http_Delete(self); return r; } } #else MI_UNUSED(https_port); #endif return MI_RESULT_OK; } MI_Result Http_Delete( Http* self) { /* Check parameters */ if (!self) return MI_RESULT_INVALID_PARAMETER; /* Check magic number */ if (self->magic != _MAGIC) return MI_RESULT_INVALID_PARAMETER; if (self->internalSelectorUsed) { /* Release selector; Note: selector-destory closes all sockects in a list including connector and listener */ Selector_Destroy(self->selector); /* Shutdown the network */ Sock_Stop(); } if (self->sslContext) SSL_CTX_free(self->sslContext); /* Clear magic number */ self->magic = 0xDDDDDDDD; /* Free self pointer */ free(self); return MI_RESULT_OK; } MI_Result Http_Run( Http* self, MI_Uint64 timeoutUsec) { /* Run the selector */ return Selector_Run(self->selector, timeoutUsec); } /* sends 'ok' response with provided content; if message is accepted to be sent, on return *data == null (taking memory ownership)*/ MI_Result Http_SendResponse( Http* self, void* httpConnectionHanlde, int httpErrorCode, Page** data) { Http_SR_SocketData* sendSock; /* check params */ if (!self) return MI_RESULT_INVALID_PARAMETER; if (self->magic != _MAGIC) { LOGW((T("_SendIN_IO_thread: invalid magic!") )); return MI_RESULT_INVALID_PARAMETER; } sendSock = (Http_SR_SocketData*)httpConnectionHanlde; /* validate handler */ if (MI_RESULT_OK != Selector_ContainsHandler( self->selector, (Handler*)sendSock ) ) { LOGW((T("cannot send message: invalid handler (msg->clientID) %p\n"), sendSock)); return MI_RESULT_INVALID_PARAMETER; } sendSock->requestIsBeingProcessed = MI_FALSE; sendSock->base.mask |= SELECTOR_WRITE; sendSock->base.mask &= ~SELECTOR_READ; if (data) { sendSock->sendPage = *data; *data = 0; } else { sendSock->sendPage = 0; } sendSock->httpErrorCode = httpErrorCode; sendSock->sentSize = 0; sendSock->sendingState = RECV_STATE_HEADER; _RequestCallbackWrite(sendSock); return MI_RESULT_OK; } /* Sets http options (mostly unit-test support) */ MI_Result Http_SetOptions( Http* self, const HttpOptions* options) { /* check params */ if (!self || !options) return MI_RESULT_INVALID_PARAMETER; if (self->magic != _MAGIC) { LOGW((T("Http_SetOptions: invalid magic!") )); return MI_RESULT_INVALID_PARAMETER; } self->options = *options; return MI_RESULT_OK; }