PKI_HTTP *PKI_HTTP_get_message (const PKI_SOCKET * sock, int timeout, size_t max_size) { PKI_HTTP * ret = NULL; char * eoh = NULL; char * body = NULL; ssize_t read = 0; // Keeps track of the single reading ssize_t free = 0; // Keeps track of the remaining buffer space ssize_t idx = 0; // Keeps track of how much data we poured into MEM ssize_t size = 0; // Keeps track of the read data from socket // Let's initialize some useful variables (code readability) long long content_length = -1; // Buffer where to keep the data PKI_MEM *m = NULL; // Allocates the HTTP message container if ((ret = PKI_HTTP_new()) == NULL) { PKI_ERROR(PKI_ERR_MEMORY_ALLOC, NULL ); goto err; } ret->method = PKI_HTTP_METHOD_UNKNOWN; if (max_size > 0) { // Allocates a new MEM object m = PKI_MEM_new(max_size + 1); } else { // Allocates the default buffer for HTTP messages m = PKI_MEM_new(HTTP_BUF_SIZE + 1); } if (m == NULL) { PKI_ERROR(PKI_ERR_MEMORY_ALLOC, NULL); return NULL; } // Sets the free space in the buffer free = (ssize_t) m->size - 1; // Let's retrieve the data from the socket. Note that this for // always read at most 'free' bytes which carries the amount of // free space in the buffer -> safe for (read = PKI_SOCKET_read(sock, (char *)(&(m->data[idx])), (size_t) free, timeout); read > 0; read = PKI_SOCKET_read(sock, (char *)(&(m->data[idx])), (size_t) free, timeout)) { // If read is negative, there was an error on the socket // let's just report it as an error and move on if (read < 0) { if (!eoh) { PKI_log_err("Error while reading from socket"); goto err; } else { // Nothing to read anymore - let's break PKI_log_err("Nothing to read anymore (read = %d)", read); break; } } else if (read == 0 && eoh) { // No data was read, let's assume the stream is complete and // break from the for loop break; } // Let's be sure there is a NULL-bound limit to the read data size += read; free -= read; m->data[size] = '\x0'; // If we don't have a header yet, let's look for it if (!eoh && ((eoh = __find_end_of_header(m, idx)) != NULL)) { // We want the header to finish with just one '\r\n' - since the // pointer we receive is at the end of the '\r\n\r\n' sequence, // we need to shrink by 2 bytes size_t header_size = (size_t) (eoh - (char *) m->data - 2); ret->head = PKI_MEM_new_data(header_size + 1, m->data); ret->head->data[header_size] = '\x0'; // If we can not parse the header - we have to return error if (PKI_ERR == __parse_http_header(ret)) goto err; // Let's get the pointer to the start of the body body = eoh + 1; // Checks for the content-length is in the header - if we have not found it, yet if (ret->method != PKI_HTTP_METHOD_GET && content_length < 0) { char *cnt_len_s = NULL; if ((cnt_len_s = PKI_HTTP_get_header(ret, "Content-Length" )) != NULL) { content_length = atoll(cnt_len_s); PKI_Free(cnt_len_s); // PKI_log_debug ( "HTTP Content-Length: %d bytes", content_length); } } } // End of if (!eoh) ... // Updates the start pointer for the next read operation idx += read; // Let's check if we need to expand the buffer if (max_size <= 0) { // We expand the mem if the buffer has less than 2K free if (free < 2048) { ssize_t ofs = 0; if(body) { ofs = (ssize_t)(body - (char *)m->data); if(ofs < 0) { PKI_log_debug ( "Invalid offset for HTTP body: Start: %p - Body: %p", m->data, body); PKI_ERROR(PKI_ERR_URI_READ, NULL); goto err; } } // Grow the memory for the HTTP message if(content_length > 0 && body && m->size < (size_t)(content_length + ofs)) { size_t len = ((size_t)(content_length + ofs) - m->size); if (PKI_MEM_grow(m, len + 1) == PKI_ERR) { PKI_ERROR(PKI_ERR_MEMORY_ALLOC, NULL); goto err; } free += (ssize_t)len; } else { if (PKI_MEM_grow(m, HTTP_BUF_SIZE) == PKI_ERR) { PKI_ERROR(PKI_ERR_MEMORY_ALLOC, NULL); goto err; } free += HTTP_BUF_SIZE; } // Let's update the pointer to the body if(body) body = (char *)m->data + ofs; } } // Let's check if we need to perform the next read or not if (eoh && ret->method == PKI_HTTP_METHOD_GET) { // We do not need to wait for any other read as GETs do not have // a full body break; } else if ((content_length >= 0) && (&m->data[size] - (unsigned char *)body >= content_length)) { // Here we have received the full body (since the size of the body corresponds or exceeds the // contents of the Content-Length: header line), therefore we can safely get out of the cycle break; } } /* End of for..loop */ // Here we should have both the eoh and the body - if not, there was // an error and we return the malformed request message if (!eoh) { // PKI_log_err ( "Read data (so far): %d bytes - Last read: %d bytes", idx, read); PKI_ERROR(PKI_ERR_URI_READ, NULL); goto err; } // Sets some HTTP specific data ret->location = PKI_HTTP_get_header ( ret, "Location" ); ret->type = PKI_HTTP_get_header ( ret, "Content-Type" ); if (ret->method != PKI_HTTP_METHOD_GET && content_length > 0 && body) { ssize_t body_start = (ssize_t)(body - (char *)m->data); ssize_t body_size = idx - body_start; if (body_start < 0 || body_size < 0) { PKI_log_err ( "Invalid offset for HTTP body - body_start: %d bytes - body_size: %d bytes", body_start, body_size); PKI_ERROR(PKI_ERR_URI_READ, NULL); goto err; } //Check if Content-Length > 0 but body_size is 0 if (body_size == 0) goto err; // Let's allocate the body for the HTTP message (if any) ret->body = PKI_MEM_new_data((size_t)body_size+1, (unsigned char *)body); if(ret->body == NULL) { PKI_ERROR(PKI_ERR_MEMORY_ALLOC, NULL); goto err; } ret->body->size = (size_t) body_size; } else { ret->body = PKI_MEM_new_null(); } // Let's free the buffer memory if (m) PKI_MEM_free(m); // Now we can return the HTTP message return ret; err: // First we free the return message if (ret) PKI_HTTP_free(ret); // We then free the buffer memory object if (m) PKI_MEM_free(m); return NULL; }
int PKI_HTTP_get_socket (const PKI_SOCKET * sock, const char * data, size_t data_size, const char * content_type, int method, int timeout, size_t max_size, PKI_MEM_STACK ** sk ) { size_t len = 0; const char *my_cont_type = "application/unknown"; PKI_HTTP *http_rv = NULL; int rv = -1; int ret = PKI_OK; size_t max_len = 0; size_t auth_len = 0; char *tmp = NULL; char *auth_tmp = NULL; char *head_get = "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "User-Agent: LibPKI\r\n" "Connection: close\r\n" "%s"; char *head_post = "POST %s HTTP/1.1\r\n" "Host: %s\r\n" "User-Agent: LibPKI\r\n" "Connection: close\r\n" "Content-type: %s\r\n" "Content-Length: %d\r\n" "%s"; char *head = NULL; if ( timeout < 0 ) timeout = 0; if ( !sock || !sock->url ) return PKI_ERR; // Process the authentication information if provided by the caller if (sock->url && sock->url->usr && sock->url->pwd) { // Rough estimate for the auth string max_len = strlen(sock->url->usr) + strlen(sock->url->pwd) + 100; // Special case for when a usr/pwd was specified in the URL auth_tmp = PKI_Malloc(len); auth_len = (size_t)snprintf(auth_tmp, len, "Authentication: user %s:%s\r\n\r\n", sock->url->usr, sock->url->pwd); } else { // If we do not have the auth info, we just add the end of header auth_len = 2; auth_tmp = "\r\n"; } if (method == PKI_HTTP_METHOD_GET) { // Gets the right header head = head_get; // Estimate the header's final size max_len = strlen(head) + strlen(sock->url->path) + strlen(sock->url->addr) + 101; // Allocates enough space for the header tmp = PKI_Malloc ( max_len + auth_len ); // Prints the header into the tmp container len = (size_t) snprintf(tmp, max_len, head, sock->url->path, sock->url->addr, auth_tmp); } else if (method == PKI_HTTP_METHOD_POST) { // Gets the right head head = head_post; // Determines the right content type if ( content_type ) my_cont_type = content_type; else my_cont_type = "text/html"; // Checks the max len for the allocated header max_len = strlen(head) + strlen(sock->url->path) + strlen(sock->url->addr) + strlen(my_cont_type) + 101; // Allocates the memory for the header tmp = PKI_Malloc ( max_len + auth_len ); // Prints the header into the tmp container len = (size_t) snprintf(tmp, max_len, head, sock->url->path, sock->url->addr, my_cont_type, data_size, auth_tmp ); } else { PKI_log_err ( "Method (%d) not supported!", method ); return PKI_ERR; } // PKI_MEM *r = PKI_MEM_new_data(len, tmp); // URL_put_data("file://http_req.txt", r, NULL, NULL, 0, 0, NULL); // PKI_MEM_free(r); if ((rv = (int) PKI_SOCKET_write(sock, tmp, len)) < 0) { PKI_log_err("Can not write HTTP header to socket"); PKI_Free(tmp); goto err; } // Free the tmp pointer that held the request header if (tmp) PKI_Free (tmp); // If we were using a POST method, we need to actually send the data if(data != NULL) { PKI_log_err("{DEBUG} Writing Data -> data_size = %d, data = %p", data_size, data); if ((PKI_SOCKET_write(sock, data, data_size)) < 0) { PKI_log_err ("Can not write POST to socket."); goto err; } } // Let's now wait for the response from the server if ((http_rv = PKI_HTTP_get_message(sock, timeout, max_size)) == NULL) { PKI_log_err ("HTTP retrieval error\n"); goto err; } // We shall now check for the return code if (http_rv->code >= 400 ) { goto err; } else if (http_rv->code >= 300) { /* Redirection - let's try that */ if (http_rv->location == NULL) { PKI_log_debug ( "HTTP Redirection but no location provided!"); goto err; } PKI_log_debug("HTTP Redirection Detected [URL: %s]", http_rv->location ); if (strstr(http_rv->location, "://") != NULL) { URL *url_tmp = NULL; if( strncmp_nocase( http_rv->location, sock->url->url_s, (int) strlen(http_rv->location)) == 0) { PKI_log_debug( "HTTP cyclic redirection!"); goto err; } if ((url_tmp = URL_new ( http_rv->location )) == NULL) { PKI_log_debug("HTTP location is not a valid URI (%s)", http_rv->location ); goto err; } if ( sock->url->ssl == 0 ) { ret = PKI_HTTP_get_url ( url_tmp, data, data_size, content_type, method, timeout, max_size, sk, NULL ); } else { PKI_SSL *ssl2 = PKI_SSL_dup ( sock->ssl ); ret = PKI_HTTP_get_url ( url_tmp, data, data_size, content_type, method, timeout, max_size, sk, ssl2 ); } if ( url_tmp ) URL_free ( url_tmp ); goto end; } else { const char *prot_s = NULL; char new_url[2048]; URL *my_new_url = NULL; PKI_SSL *ssl2 = PKI_SSL_dup ( sock->ssl ); prot_s = URL_proto_to_string ( sock->url->proto ); if( !prot_s ) goto err; snprintf(new_url, sizeof(new_url),"%s://%s%s", prot_s, sock->url->addr, http_rv->location ); if( strncmp_nocase( new_url, sock->url->url_s, (int) strlen ( new_url )) == 0 ) { PKI_log_debug( "HTTP cyclic redirection!"); goto err; } my_new_url = URL_new ( new_url ); ret = PKI_HTTP_get_url ( my_new_url, data, data_size, content_type, method, timeout, max_size, sk, ssl2 ); if (ssl2) PKI_SSL_free ( ssl2 ); } } else if (http_rv->code != 200) { PKI_log_debug( "Unknown HTTP Return code [Code: %d]", http_rv->code ); goto err; } /* PKI_log_err("{DEBUG} method = %d, header->size = %d, body = %p, body_size = %d", http_rv->method, http_rv->head->size, http_rv->body, http_rv->body->size); URL_put_data("file://http-resp-header.txt", http_rv->head, NULL, NULL, 0, 0, NULL); URL_put_data("file://http-resp-data.txt", http_rv->body, NULL, NULL, 0, 0, NULL); */ // If a Pointer was provided, we want the data back if (sk) { // Checks if the caller provided an already allocated data // structure. If not, we allocate it. if (*sk) PKI_STACK_MEM_free_all(*sk); // Allocates a new structure if ((*sk = PKI_STACK_MEM_new()) == NULL) { // If a memory error occurs report it and exit PKI_ERROR(PKI_ERR_MEMORY_ALLOC, NULL); // Nothing more to do goto err; } // Add the returned value to the stack if (PKI_STACK_MEM_push(*sk, http_rv->body) != PKI_OK) { PKI_log_err("Can not push the HTTP result body in the result stack"); goto err; } // Remove ownership of the body PKI_MEM from the original // HTTP msg container http_rv->body = NULL; } end: // Finally free the HTTP message memory if (http_rv) PKI_HTTP_free(http_rv); // Returns the result return ret; err: // Error condition if (http_rv) PKI_HTTP_free ( http_rv ); // Free the locally allocated memory if (*sk) PKI_STACK_MEM_free_all(*sk); *sk = NULL; return PKI_ERR; }
PKI_X509_OCSP_REQ * ocspd_req_get_socket ( int connfd, OCSPD_CONFIG *ocspd_conf) { PKI_X509_OCSP_REQ *req = NULL; PKI_X509_OCSP_REQ_VALUE *req_val = NULL; PKI_IO *mem = NULL; PKI_MEM *pathmem = NULL; PKI_MEM *b64mem = NULL; PKI_SOCKET sock; size_t maxsize = 0; maxsize = (size_t) ocspd_conf->max_req_size; PKI_HTTP *http_msg = NULL; if ( connfd <= 0 ) return NULL; // Initialize the sock structure sock.ssl = NULL; PKI_SOCKET_set_fd ( &sock, connfd ); http_msg = PKI_HTTP_get_message(&sock, (int) ocspd_conf->max_timeout_secs, maxsize); if (http_msg == NULL) { PKI_log_err ("Network Error while reading Request!"); return NULL; }; /* If method is METHOD_GET we shall de-urlify the buffer and get the right begin (keep in mind there might be a path set in the config */ if( http_msg->method == PKI_HTTP_METHOD_GET ) { char *req_pnt = NULL; if (http_msg->path == NULL) { PKI_log_err("Malformed GET request"); goto err; } req_pnt = http_msg->path; while(strchr(req_pnt, '/') != NULL) { req_pnt = strchr(req_pnt, '/') + 1; } pathmem = PKI_MEM_new_data(strlen(req_pnt), (unsigned char *) req_pnt); if (pathmem == NULL) { PKI_log_err("Memory Allocation Error!"); goto err; } if((b64mem = PKI_MEM_url_decode (pathmem, 0)) == NULL) { PKI_log_err("Memory Allocation Error!"); PKI_MEM_free(pathmem); pathmem = NULL; // Safety goto err; } if (PKI_MEM_B64_decode(b64mem, 76) == PKI_ERR ) { PKI_log_err ("Error decoding B64 Mem"); PKI_MEM_free ( b64mem ); b64mem = NULL; req_pnt = http_msg->path; while(req_pnt[0] == '/') { req_pnt=req_pnt + 1; } b64mem = PKI_MEM_new_data(strlen(req_pnt), (unsigned char *) req_pnt); if (b64mem == NULL) { PKI_log_err("Memory Allocation Error!"); goto err; } if (PKI_MEM_B64_decode(b64mem, 76) == PKI_ERR ) { PKI_log_err ("Error decoding B64 Mem"); PKI_MEM_free ( b64mem ); goto err; } } if((mem = BIO_new_mem_buf(b64mem->data, (int) b64mem->size )) == NULL) { PKI_log_err("Memory Allocation Error"); PKI_MEM_free ( b64mem ); goto err; } if((req_val = d2i_OCSP_REQ_bio(mem, NULL)) == NULL ) { PKI_log_err("Can not parse REQ"); } PKI_MEM_free ( b64mem ); BIO_free (mem); } else if ( http_msg->method == PKI_HTTP_METHOD_POST) { mem = BIO_new_mem_buf(http_msg->body->data, (int) http_msg->body->size); if (mem == NULL) { PKI_log_err( "Memory Allocation Error"); goto err; } else { if ((req_val = d2i_OCSP_REQ_bio(mem, NULL)) == NULL) { PKI_log_err("Can not parse REQ"); } BIO_free (mem); } } else { PKI_log_err ( "HTTP Method not supported"); goto err; } if ( !req_val ) goto err; req = PKI_X509_new_value(PKI_DATATYPE_X509_OCSP_REQ, req_val, NULL); if (req == NULL) { PKI_log_err ("Can not generate a new X509_OCSP_REQ"); goto err; } if ( http_msg ) PKI_HTTP_free ( http_msg ); return (req); err: if (http_msg) PKI_HTTP_free(http_msg); return NULL; }