LCOV - code coverage report
Current view: top level - libcli/http - http.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 401 622 64.5 %
Date: 2024-04-21 15:09:00 Functions: 24 27 88.9 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    HTTP library
       5             : 
       6             :    Copyright (C) 2013 Samuel Cabrero <samuelcabrero@kernevil.me>
       7             : 
       8             :    This program is free software; you can redistribute it and/or modify
       9             :    it under the terms of the GNU General Public License as published by
      10             :    the Free Software Foundation; either version 3 of the License, or
      11             :    (at your option) any later version.
      12             : 
      13             :    This program is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :    GNU General Public License for more details.
      17             : 
      18             :    You should have received a copy of the GNU General Public License
      19             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : #include "includes.h"
      23             : #include "lib/util/tevent_ntstatus.h"
      24             : #include "http.h"
      25             : #include "http_internal.h"
      26             : #include "util/tevent_werror.h"
      27             : #include "lib/util/dlinklist.h"
      28             : 
      29             : #undef strcasecmp
      30             : 
      31             : enum http_body_type {
      32             :         BODY_NONE = 0,
      33             :         BODY_CONTENT_LENGTH,
      34             :         BODY_CHUNKED,
      35             :         BODY_ERROR = -1
      36             : };
      37             : 
      38             : /**
      39             :  * Determines if a response should have a body.
      40             :  * @return 2 if response MUST use chunked encoding,
      41             :  *         1 if the response MUST have a body;
      42             :  *         0 if the response MUST NOT have a body.
      43             :  * Returns -1 on error.
      44             :  */
      45          20 : static enum http_body_type http_response_needs_body(
      46             :                                         struct http_request *req)
      47             : {
      48          20 :         struct http_header *h = NULL;
      49             : 
      50          20 :         if (!req) {
      51           0 :                 return BODY_ERROR;
      52             :         }
      53             : 
      54          80 :         for (h = req->headers; h != NULL; h = h->next) {
      55           0 :                 int cmp;
      56           0 :                 int n;
      57           0 :                 char c;
      58           0 :                 unsigned long long v;
      59             : 
      60          80 :                 cmp = strcasecmp(h->key, "Transfer-Encoding");
      61          80 :                 if (cmp == 0) {
      62          10 :                         cmp = strcasecmp(h->value, "chunked");
      63          10 :                         if (cmp == 0) {
      64          20 :                                 return BODY_CHUNKED;
      65             :                         }
      66             :                         /* unsupported Transfer-Encoding type */
      67           2 :                         DBG_ERR("Unsupported transfer encoding type %s\n",
      68             :                                 h->value);
      69           2 :                         return BODY_ERROR;
      70             :                 }
      71             : 
      72          70 :                 cmp = strcasecmp(h->key, "Content-Length");
      73          70 :                 if (cmp != 0) {
      74          60 :                         continue;
      75             :                 }
      76             : 
      77          10 :                 n = sscanf(h->value, "%llu%c", &v, &c);
      78          10 :                 if (n != 1) {
      79           0 :                         return BODY_ERROR;
      80             :                 }
      81             : 
      82          10 :                 req->remaining_content_length = v;
      83             : 
      84          10 :                 if (v != 0) {
      85          10 :                         return BODY_CONTENT_LENGTH;
      86             :                 }
      87             : 
      88           0 :                 return BODY_NONE;
      89             :         }
      90             : 
      91           0 :         return BODY_NONE;
      92             : }
      93             : struct http_chunk
      94             : {
      95             :         struct http_chunk *prev, *next;
      96             :         DATA_BLOB blob;
      97             : };
      98             : 
      99             : struct http_read_response_state {
     100             :         enum http_parser_state  parser_state;
     101             :         size_t                  max_headers_size;
     102             :         uint64_t                max_content_length;
     103             :         DATA_BLOB               buffer;
     104             :         struct http_request     *response;
     105             :         struct http_chunk       *chunks;
     106             : };
     107             : 
     108             : /**
     109             :  * Parses the HTTP headers
     110             :  */
     111        2938 : static enum http_read_status http_parse_headers(struct http_read_response_state *state)
     112             : {
     113        2938 :         enum http_read_status   status = HTTP_ALL_DATA_READ;
     114        2938 :         char                    *ptr = NULL;
     115        2938 :         char                    *line = NULL;
     116        2938 :         char                    *key = NULL;
     117        2938 :         char                    *value = NULL;
     118        2938 :         int                     n = 0;
     119           0 :         enum http_body_type     ret;
     120             : 
     121             :         /* Sanity checks */
     122        2938 :         if (!state || !state->response) {
     123           0 :                 DEBUG(0, ("%s: Invalid Parameter\n", __func__));
     124           0 :                 return HTTP_DATA_CORRUPTED;
     125             :         }
     126             : 
     127        2938 :         if (state->buffer.length > state->max_headers_size) {
     128           0 :                 DEBUG(0, ("%s: Headers too long: %zi, maximum length is %zi\n", __func__,
     129             :                           state->buffer.length, state->max_headers_size));
     130           0 :                 return HTTP_DATA_TOO_LONG;
     131             :         }
     132             : 
     133        2938 :         line = talloc_strndup(state, (char *)state->buffer.data, state->buffer.length);
     134        2938 :         if (!line) {
     135           0 :                 DEBUG(0, ("%s: Memory error\n", __func__));
     136           0 :                 return HTTP_DATA_CORRUPTED;
     137             :         }
     138             : 
     139        2938 :         ptr = strstr(line, "\r\n");
     140        2938 :         if (ptr == NULL) {
     141        2838 :                 TALLOC_FREE(line);
     142        2838 :                 return HTTP_MORE_DATA_EXPECTED;
     143             :         }
     144             : 
     145         100 :         state->response->headers_size += state->buffer.length;
     146             : 
     147         100 :         if (strncmp(line, "\r\n", 2) == 0) {
     148          20 :                 DEBUG(11,("%s: All headers read\n", __func__));
     149             : 
     150          20 :                 ret = http_response_needs_body(state->response);
     151          20 :                 switch (ret) {
     152           8 :                 case BODY_CHUNKED:
     153           8 :                         DEBUG(11, ("%s: need to process chunks... %d\n", __func__,
     154             :                                    state->response->response_code));
     155           8 :                         state->parser_state = HTTP_READING_CHUNK_SIZE;
     156           8 :                         break;
     157          10 :                 case BODY_CONTENT_LENGTH:
     158          10 :                         if (state->response->remaining_content_length <= state->max_content_length) {
     159           8 :                                 DEBUG(11, ("%s: Start of read body\n", __func__));
     160           8 :                                 state->parser_state = HTTP_READING_BODY;
     161           8 :                                 break;
     162             :                         }
     163           0 :                         FALL_THROUGH;
     164             :                 case BODY_NONE:
     165           2 :                         DEBUG(11, ("%s: Skipping body for code %d\n", __func__,
     166             :                                    state->response->response_code));
     167           2 :                         state->parser_state = HTTP_READING_DONE;
     168           2 :                         break;
     169           2 :                 case BODY_ERROR:
     170           2 :                         DEBUG(0, ("%s_: Error in http_response_needs_body\n", __func__));
     171           2 :                         TALLOC_FREE(line);
     172           2 :                         return HTTP_DATA_CORRUPTED;
     173           0 :                         break;
     174             :                 }
     175             : 
     176          18 :                 TALLOC_FREE(line);
     177          18 :                 return HTTP_ALL_DATA_READ;
     178             :         }
     179             : 
     180          80 :         n = sscanf(line, "%m[^:]: %m[^\r\n]\r\n", &key, &value);
     181          80 :         if (n != 2) {
     182           0 :                 DEBUG(0, ("%s: Error parsing header '%s'\n", __func__, line));
     183           0 :                 status = HTTP_DATA_CORRUPTED;
     184           0 :                 goto error;
     185             :         }
     186             : 
     187          80 :         if (http_add_header(state->response, &state->response->headers, key, value) == -1) {
     188           0 :                 DEBUG(0, ("%s: Error adding header\n", __func__));
     189           0 :                 status = HTTP_DATA_CORRUPTED;
     190           0 :                 goto error;
     191             :         }
     192             : 
     193          80 : error:
     194          80 :         free(key);
     195          80 :         free(value);
     196          80 :         TALLOC_FREE(line);
     197          80 :         return status;
     198             : }
     199             : 
     200           6 : static bool http_response_process_chunks(struct http_read_response_state *state)
     201             : {
     202           6 :         struct http_chunk *chunk = NULL;
     203           6 :         struct http_request *resp = state->response;
     204             : 
     205          28 :         for (chunk = state->chunks; chunk; chunk = chunk->next) {
     206          22 :                 DBG_DEBUG("processing chunk of size %zi\n",
     207             :                           chunk->blob.length);
     208          22 :                 if (resp->body.data == NULL) {
     209           6 :                         resp->body = chunk->blob;
     210           6 :                         chunk->blob = data_blob_null;
     211           6 :                         talloc_steal(resp, resp->body.data);
     212           6 :                         continue;
     213             :                 }
     214             : 
     215          16 :                 resp->body.data =
     216          16 :                         talloc_realloc(resp,
     217             :                                 resp->body.data,
     218             :                                 uint8_t,
     219             :                                 resp->body.length + chunk->blob.length);
     220          16 :                 if (!resp->body.data) {
     221           0 :                                 return false;
     222             :                 }
     223          16 :                 memcpy(resp->body.data + resp->body.length,
     224          16 :                        chunk->blob.data,
     225             :                        chunk->blob.length);
     226             : 
     227          16 :                 resp->body.length += chunk->blob.length;
     228             : 
     229          16 :                 TALLOC_FREE(chunk->blob.data);
     230          16 :                 chunk->blob = data_blob_null;
     231             :         }
     232           6 :         return true;
     233             : }
     234             : 
     235          72 : static enum http_read_status http_read_chunk_term(struct http_read_response_state *state)
     236             : {
     237          72 :         enum http_read_status   status = HTTP_ALL_DATA_READ;
     238          72 :         char                    *ptr = NULL;
     239          72 :         char                    *line = NULL;
     240             : 
     241             :         /* Sanity checks */
     242          72 :         if (!state || !state->response) {
     243           0 :                 DBG_ERR("%s: Invalid Parameter\n", __func__);
     244           0 :                 return HTTP_DATA_CORRUPTED;
     245             :         }
     246             : 
     247          72 :         line = talloc_strndup(state, (char *)state->buffer.data, state->buffer.length);
     248          72 :         if (!line) {
     249           0 :                 DBG_ERR("%s: Memory error\n", __func__);
     250           0 :                 return HTTP_DATA_CORRUPTED;
     251             :         }
     252          72 :         ptr = strstr(line, "\r\n");
     253          72 :         if (ptr == NULL) {
     254          36 :                 TALLOC_FREE(line);
     255          36 :                 return HTTP_MORE_DATA_EXPECTED;
     256             :         }
     257             : 
     258          36 :         if (strncmp(line, "\r\n", 2) == 0) {
     259             :                 /* chunk terminator */
     260          36 :                 if (state->parser_state == HTTP_READING_FINAL_CHUNK_TERM) {
     261           6 :                         if (http_response_process_chunks(state) == false) {
     262           0 :                                 status = HTTP_DATA_CORRUPTED;
     263           0 :                                 goto out;
     264             :                         }
     265           6 :                         state->parser_state = HTTP_READING_DONE;
     266             :                 } else {
     267          30 :                         state->parser_state = HTTP_READING_CHUNK_SIZE;
     268             :                 }
     269          36 :                 status = HTTP_ALL_DATA_READ;
     270          36 :                 goto out;
     271             :         }
     272             : 
     273           0 :         status = HTTP_DATA_CORRUPTED;
     274          36 : out:
     275          36 :         TALLOC_FREE(line);
     276          36 :         return status;
     277             : }
     278             : 
     279         114 : static enum http_read_status http_read_chunk_size(struct http_read_response_state *state)
     280             : {
     281         114 :         enum http_read_status   status = HTTP_ALL_DATA_READ;
     282         114 :         char                    *ptr = NULL;
     283         114 :         char                    *line = NULL;
     284         114 :         char                    *value = NULL;
     285         114 :         int                     n = 0;
     286           0 :         unsigned long long v;
     287             : 
     288             :         /* Sanity checks */
     289         114 :         if (!state || !state->response) {
     290           0 :                 DBG_ERR("%s: Invalid Parameter\n", __func__);
     291           0 :                 return HTTP_DATA_CORRUPTED;
     292             :         }
     293             : 
     294         114 :         line = talloc_strndup(state, (char *)state->buffer.data, state->buffer.length);
     295         114 :         if (!line) {
     296           0 :                 DBG_ERR("%s: Memory error\n", __func__);
     297           0 :                 return HTTP_DATA_CORRUPTED;
     298             :         }
     299         114 :         ptr = strstr(line, "\r\n");
     300         114 :         if (ptr == NULL) {
     301          76 :                 TALLOC_FREE(line);
     302          76 :                 return HTTP_MORE_DATA_EXPECTED;
     303             :         }
     304             : 
     305          38 :         n = sscanf(line, "%m[^\r\n]\r\n", &value);
     306          38 :         if (n != 1) {
     307           0 :                 DBG_ERR("%s: Error parsing chunk size '%s'\n", __func__, line);
     308           0 :                 status = HTTP_DATA_CORRUPTED;
     309           0 :                 goto out;
     310             :         }
     311             : 
     312          38 :         DBG_DEBUG("Got chunk size string %s\n", value);
     313          38 :         n = sscanf(value, "%llx", &v);
     314          38 :         if (n != 1) {
     315           0 :                 DBG_ERR("%s: Error parsing chunk size '%s'\n", __func__, line);
     316           0 :                 status = HTTP_DATA_CORRUPTED;
     317           0 :                 goto out;
     318             :         }
     319          38 :         DBG_DEBUG("Got chunk size %llu 0x%llx\n", v, v);
     320          38 :         if (v == 0) {
     321           6 :                 state->parser_state = HTTP_READING_FINAL_CHUNK_TERM;
     322             :         } else {
     323          32 :                 state->parser_state = HTTP_READING_CHUNK;
     324             :         }
     325          38 :         state->response->remaining_content_length = v;
     326          38 :         status = HTTP_ALL_DATA_READ;
     327          38 : out:
     328          38 :         if (value) {
     329          38 :                 free(value);
     330             :         }
     331          38 :         TALLOC_FREE(line);
     332          38 :         return status;
     333             : }
     334             : 
     335             : /**
     336             :  * Parses the first line of a HTTP response
     337             :  */
     338          20 : static bool http_parse_response_line(struct http_read_response_state *state)
     339             : {
     340          20 :         bool    status = true;
     341           0 :         char    *protocol;
     342          20 :         char    *msg = NULL;
     343           0 :         char    major;
     344           0 :         char    minor;
     345           0 :         int     code;
     346          20 :         char    *line = NULL;
     347           0 :         int     n;
     348             : 
     349             :         /* Sanity checks */
     350          20 :         if (!state) {
     351           0 :                 DEBUG(0, ("%s: Input parameter is NULL\n", __func__));
     352           0 :                 return false;
     353             :         }
     354             : 
     355          20 :         line = talloc_strndup(state, (char*)state->buffer.data, state->buffer.length);
     356          20 :         if (!line) {
     357           0 :                 DEBUG(0, ("%s: Memory error\n", __func__));
     358           0 :                 return false;
     359             :         }
     360             : 
     361          20 :         n = sscanf(line, "%m[^/]/%c.%c %d %m[^\r\n]\r\n",
     362             :                    &protocol, &major, &minor, &code, &msg);
     363             : 
     364          20 :         DEBUG(11, ("%s: Header parsed(%i): protocol->%s, major->%c, minor->%c, "
     365             :                    "code->%d, message->%s\n", __func__, n, protocol, major, minor,
     366             :                    code, msg));
     367             : 
     368          20 :         if (n != 5) {
     369           0 :                 DEBUG(0, ("%s: Error parsing header\n",       __func__));
     370           0 :                 status = false;
     371           0 :                 goto error;
     372             :         }
     373             : 
     374          20 :         if (major != '1') {
     375           0 :                 DEBUG(0, ("%s: Bad HTTP major number '%c'\n", __func__, major));
     376           0 :                 status = false;
     377           0 :                 goto error;
     378             :         }
     379             : 
     380          20 :         if (code == 0) {
     381           0 :                 DEBUG(0, ("%s: Bad response code '%d'\n", __func__, code));
     382           0 :                 status = false;
     383           0 :                 goto error;
     384             :         }
     385             : 
     386          20 :         if (msg == NULL) {
     387           0 :                 DEBUG(0, ("%s: Error parsing HTTP data\n", __func__));
     388           0 :                 status = false;
     389           0 :                 goto error;
     390             :         }
     391             : 
     392          20 :         state->response->major = major;
     393          20 :         state->response->minor = minor;
     394          20 :         state->response->response_code = code;
     395          20 :         state->response->response_code_line = talloc_strndup(state->response,
     396             :                                                              msg, strlen(msg));
     397             : 
     398          20 : error:
     399          20 :         free(protocol);
     400          20 :         free(msg);
     401          20 :         TALLOC_FREE(line);
     402          20 :         return status;
     403             : }
     404             : 
     405             : /*
     406             :  * Parses header lines from a request or a response into the specified
     407             :  * request object given a buffer.
     408             :  *
     409             :  * Returns
     410             :  *   HTTP_DATA_CORRUPTED                on error
     411             :  *   HTTP_MORE_DATA_EXPECTED    when we need to read more headers
     412             :  *   HTTP_DATA_TOO_LONG                 on error
     413             :  *   HTTP_ALL_DATA_READ                 when all headers have been read
     414             :  */
     415         340 : static enum http_read_status http_parse_firstline(struct http_read_response_state *state)
     416             : {
     417         340 :         enum http_read_status   status = HTTP_ALL_DATA_READ;
     418         340 :         char                    *ptr = NULL;
     419           0 :         char                    *line;
     420             : 
     421             :         /* Sanity checks */
     422         340 :         if (!state) {
     423           0 :                 DEBUG(0, ("%s: Invalid Parameter\n", __func__));
     424           0 :                 return HTTP_DATA_CORRUPTED;
     425             :         }
     426             : 
     427         340 :         if (state->buffer.length > state->max_headers_size) {
     428           0 :                 DEBUG(0, ("%s: Headers too long: %zi, maximum length is %zi\n", __func__,
     429             :                           state->buffer.length, state->max_headers_size));
     430           0 :                 return HTTP_DATA_TOO_LONG;
     431             :         }
     432             : 
     433         340 :         line = talloc_strndup(state, (char *)state->buffer.data, state->buffer.length);
     434         340 :         if (!line) {
     435           0 :                 DEBUG(0, ("%s: Not enough memory\n", __func__));
     436           0 :                 return HTTP_DATA_CORRUPTED;
     437             :         }
     438             : 
     439         340 :         ptr = strstr(line, "\r\n");
     440         340 :         if (ptr == NULL) {
     441         320 :                 TALLOC_FREE(line);
     442         320 :                 return HTTP_MORE_DATA_EXPECTED;
     443             :         }
     444             : 
     445          20 :         state->response->headers_size = state->buffer.length;
     446          20 :         if (!http_parse_response_line(state)) {
     447           0 :                 status = HTTP_DATA_CORRUPTED;
     448             :         }
     449             : 
     450             :         /* Next state, read HTTP headers */
     451          20 :         state->parser_state = HTTP_READING_HEADERS;
     452             : 
     453          20 :         TALLOC_FREE(line);
     454          20 :         return status;
     455             : }
     456             : 
     457          16 : static enum http_read_status http_read_body(struct http_read_response_state *state)
     458             : {
     459          16 :         struct http_request *resp = state->response;
     460             : 
     461          16 :         if (state->buffer.length < resp->remaining_content_length) {
     462           8 :                 return HTTP_MORE_DATA_EXPECTED;
     463             :         }
     464             : 
     465           8 :         resp->body = state->buffer;
     466           8 :         state->buffer = data_blob_null;
     467           8 :         talloc_steal(resp, resp->body.data);
     468           8 :         resp->remaining_content_length = 0;
     469             : 
     470           8 :         state->parser_state = HTTP_READING_DONE;
     471           8 :         return HTTP_ALL_DATA_READ;
     472             : }
     473             : 
     474          64 : static enum http_read_status http_read_chunk(struct http_read_response_state *state)
     475             : {
     476          64 :         struct http_request *resp = state->response;
     477          64 :         struct http_chunk *chunk = NULL;
     478          64 :         size_t total = 0;
     479          64 :         size_t prev = 0;
     480             : 
     481          64 :         if (state->buffer.length < resp->remaining_content_length) {
     482          32 :                 return HTTP_MORE_DATA_EXPECTED;
     483             :         }
     484             : 
     485          92 :         for (chunk = state->chunks; chunk; chunk = chunk->next) {
     486          60 :                 total += chunk->blob.length;
     487             :         }
     488             : 
     489          32 :         prev = total;
     490          32 :         total = total + state->buffer.length;
     491          32 :         if (total < prev) {
     492           0 :                 DBG_ERR("adding chunklen %zu to buf len %zu "
     493             :                         "will overflow\n",
     494             :                         state->buffer.length,
     495             :                         prev);
     496           0 :                 return HTTP_DATA_CORRUPTED;
     497             :         }
     498          32 :         if (total > state->max_content_length)  {
     499           2 :                 DBG_DEBUG("size %zu exceeds "
     500             :                           "max content len %"PRIu64" skipping body\n",
     501             :                           total,
     502             :                           state->max_content_length);
     503           2 :                 state->parser_state = HTTP_READING_DONE;
     504           2 :                 goto out;
     505             :         }
     506             : 
     507             :         /* chunk read */
     508          30 :         chunk = talloc_zero(state, struct http_chunk);
     509          30 :         if (chunk == NULL) {
     510           0 :                 DBG_ERR("%s: Memory error\n", __func__);
     511           0 :                 return HTTP_DATA_CORRUPTED;
     512             :         }
     513          30 :         chunk->blob = state->buffer;
     514          30 :         talloc_steal(chunk, chunk->blob.data);
     515          30 :         DLIST_ADD_END(state->chunks, chunk);
     516          30 :         state->parser_state = HTTP_READING_CHUNK_TERM;
     517          32 : out:
     518          32 :         state->buffer = data_blob_null;
     519          32 :         resp->remaining_content_length = 0;
     520          32 :         return HTTP_ALL_DATA_READ;
     521             : }
     522             : 
     523           0 : static enum http_read_status http_read_trailer(struct http_read_response_state *state)
     524             : {
     525           0 :         enum http_read_status status = HTTP_DATA_CORRUPTED;
     526             :         /* TODO */
     527           0 :         return status;
     528             : }
     529             : 
     530        3544 : static enum http_read_status http_parse_buffer(struct http_read_response_state *state)
     531             : {
     532        3544 :         if (!state) {
     533           0 :                 DEBUG(0, ("%s: Invalid parameter\n", __func__));
     534           0 :                 return HTTP_DATA_CORRUPTED;
     535             :         }
     536             : 
     537        3544 :         switch (state->parser_state) {
     538         340 :                 case HTTP_READING_FIRSTLINE:
     539         340 :                         return http_parse_firstline(state);
     540        2938 :                 case HTTP_READING_HEADERS:
     541        2938 :                         return http_parse_headers(state);
     542          16 :                 case HTTP_READING_BODY:
     543          16 :                         return http_read_body(state);
     544           0 :                         break;
     545          72 :                 case HTTP_READING_FINAL_CHUNK_TERM:
     546             :                 case HTTP_READING_CHUNK_TERM:
     547          72 :                         return http_read_chunk_term(state);
     548           0 :                         break;
     549         114 :                 case HTTP_READING_CHUNK_SIZE:
     550         114 :                         return http_read_chunk_size(state);
     551           0 :                         break;
     552          64 :                 case HTTP_READING_CHUNK:
     553          64 :                         return http_read_chunk(state);
     554             :                         break;
     555           0 :                 case HTTP_READING_TRAILER:
     556           0 :                         return http_read_trailer(state);
     557           0 :                         break;
     558           0 :                 case HTTP_READING_DONE:
     559             :                         /* All read */
     560           0 :                         return HTTP_ALL_DATA_READ;
     561           0 :                 default:
     562           0 :                         DEBUG(0, ("%s: Illegal parser state %d\n", __func__,
     563             :                                   state->parser_state));
     564           0 :                         break;
     565             :         }
     566           0 :         return HTTP_DATA_CORRUPTED;
     567             : }
     568             : 
     569         180 : static int http_header_is_valid_value(const char *value)
     570             : {
     571         180 :         const char      *p = NULL;
     572             : 
     573             :         /* Sanity checks */
     574         180 :         if (!value) {
     575           0 :                 DEBUG(0, ("%s: Invalid parameter\n", __func__));
     576           0 :                 return -1;
     577             :         }
     578         180 :         p = value;
     579             : 
     580         180 :         while ((p = strpbrk(p, "\r\n")) != NULL) {
     581             :                 /* Expect only one new line */
     582           0 :                 p += strspn(p, "\r\n");
     583             :                 /* Expect a space or tab for continuation */
     584           0 :                 if (*p != ' ' && *p != '\t')
     585           0 :                         return (0);
     586             :         }
     587         180 :         return 1;
     588             : }
     589             : 
     590         180 : static int http_add_header_internal(TALLOC_CTX *mem_ctx,
     591             :                                     struct http_header **headers,
     592             :                                     const char *key, const char *value,
     593             :                                     bool replace)
     594             : {
     595         180 :         struct http_header *tail = NULL;
     596         180 :         struct http_header *h = NULL;
     597             : 
     598             :         /* Sanity checks */
     599         180 :         if (!headers || !key || !value) {
     600           0 :                 DEBUG(0, ("Invalid parameter\n"));
     601           0 :                 return -1;
     602             :         }
     603             : 
     604             : 
     605             : 
     606         180 :         if (replace) {
     607           0 :                 for (h = *headers; h != NULL; h = h->next) {
     608           0 :                         if (strcasecmp(key, h->key) == 0) {
     609           0 :                                 break;
     610             :                         }
     611             :                 }
     612             : 
     613           0 :                 if (h != NULL) {
     614             :                         /* Replace header value */
     615           0 :                         if (h->value) {
     616           0 :                                 talloc_free(h->value);
     617             :                         }
     618           0 :                         h->value = talloc_strdup(h, value);
     619           0 :                         DEBUG(11, ("%s: Replaced HTTP header: key '%s', value '%s'\n",
     620             :                                         __func__, h->key, h->value));
     621           0 :                         return 0;
     622             :                 }
     623             :         }
     624             : 
     625             :         /* Add new header */
     626         180 :         h = talloc(mem_ctx, struct http_header);
     627         180 :         h->key = talloc_strdup(h, key);
     628         180 :         h->value = talloc_strdup(h, value);
     629         180 :         DLIST_ADD_END(*headers, h);
     630         180 :         tail = DLIST_TAIL(*headers);
     631         180 :         if (tail != h) {
     632           0 :                 DEBUG(0, ("%s: Error adding header\n", __func__));
     633           0 :                 return -1;
     634             :         }
     635         180 :         DEBUG(11, ("%s: Added HTTP header: key '%s', value '%s'\n",
     636             :                         __func__, h->key, h->value));
     637         180 :         return 0;
     638             : }
     639             : 
     640         180 : int http_add_header(TALLOC_CTX *mem_ctx,
     641             :                     struct http_header **headers,
     642             :                     const char *key, const char *value)
     643             : {
     644         180 :         if (strchr(key, '\r') != NULL || strchr(key, '\n') != NULL) {
     645           0 :                 DEBUG(0, ("%s: Dropping illegal header key\n", __func__));
     646           0 :                 return -1;
     647             :         }
     648             : 
     649         180 :         if (!http_header_is_valid_value(value)) {
     650           0 :                 DEBUG(0, ("%s: Dropping illegal header value\n", __func__));
     651           0 :                 return -1;
     652             :         }
     653             : 
     654         180 :         return (http_add_header_internal(mem_ctx, headers, key, value, false));
     655             : }
     656             : 
     657           0 : int http_replace_header(TALLOC_CTX *mem_ctx,
     658             :                     struct http_header **headers,
     659             :                     const char *key, const char *value)
     660             : {
     661           0 :         if (strchr(key, '\r') != NULL || strchr(key, '\n') != NULL) {
     662           0 :                 DEBUG(0, ("%s: Dropping illegal header key\n", __func__));
     663           0 :                 return -1;
     664             :         }
     665             : 
     666           0 :         if (!http_header_is_valid_value(value)) {
     667           0 :                 DEBUG(0, ("%s: Dropping illegal header value\n", __func__));
     668           0 :                 return -1;
     669             :         }
     670             : 
     671           0 :         return (http_add_header_internal(mem_ctx, headers, key, value, true));
     672             : }
     673             : 
     674             : /**
     675             :  * Remove a header from the headers list.
     676             :  *
     677             :  * Returns 0,  if the header was successfully removed.
     678             :  * Returns -1, if the header could not be found.
     679             :  */
     680           0 : int http_remove_header(struct http_header **headers, const char *key)
     681             : {
     682           0 :         struct http_header *header;
     683             : 
     684             :         /* Sanity checks */
     685           0 :         if (!headers || !key) {
     686           0 :                 DEBUG(0, ("%s: Invalid parameter\n", __func__));
     687           0 :                 return -1;
     688             :         }
     689             : 
     690           0 :         for(header = *headers; header != NULL; header = header->next) {
     691           0 :                 if (strcmp(key, header->key) == 0) {
     692           0 :                         DLIST_REMOVE(*headers, header);
     693           0 :                         return 0;
     694             :                 }
     695             :         }
     696           0 :         return -1;
     697             : }
     698             : 
     699        3564 : static int http_read_response_next_vector(struct tstream_context *stream,
     700             :                                           void *private_data,
     701             :                                           TALLOC_CTX *mem_ctx,
     702             :                                           struct iovec **_vector,
     703             :                                           size_t *_count)
     704             : {
     705           0 :         struct http_read_response_state *state;
     706           0 :         struct iovec                    *vector;
     707             : 
     708             :         /* Sanity checks */
     709        3564 :         if (!stream || !private_data || !_vector || !_count) {
     710           0 :                 DEBUG(0, ("%s: Invalid Parameter\n", __func__));
     711           0 :                 return -1;
     712             :         }
     713             : 
     714        3564 :         state = talloc_get_type_abort(private_data, struct http_read_response_state);
     715        3564 :         vector = talloc_array(mem_ctx, struct iovec, 1);
     716        3564 :         if (!vector) {
     717           0 :                 DEBUG(0, ("%s: No more memory\n", __func__));
     718           0 :                 return -1;
     719             :         }
     720             : 
     721        3564 :         if (state->buffer.data == NULL) {
     722             :                 /* Allocate buffer */
     723          20 :                 state->buffer.data = talloc_zero_array(state, uint8_t, 1);
     724          20 :                 if (!state->buffer.data) {
     725           0 :                         DEBUG(0, ("%s: No more memory\n", __func__));
     726           0 :                         return -1;
     727             :                 }
     728          20 :                 state->buffer.length = 1;
     729             : 
     730             :                 /* Return now, nothing to parse yet */
     731          20 :                 vector[0].iov_base = (void *)(state->buffer.data);
     732          20 :                 vector[0].iov_len = 1;
     733          20 :                 *_vector = vector;
     734          20 :                 *_count = 1;
     735          20 :                 return 0;
     736             :         }
     737             : 
     738        3544 :         switch (http_parse_buffer(state)) {
     739         232 :                 case HTTP_ALL_DATA_READ:
     740         232 :                         if (state->parser_state == HTTP_READING_DONE) {
     741             :                                 /* Full request or response parsed */
     742          18 :                                 *_vector = NULL;
     743          18 :                                 *_count = 0;
     744             :                         } else {
     745             :                                 /* Free current buffer and allocate new one */
     746         214 :                                 TALLOC_FREE(state->buffer.data);
     747         214 :                                 state->buffer.data = talloc_zero_array(state, uint8_t, 1);
     748         214 :                                 if (!state->buffer.data) {
     749           0 :                                         return -1;
     750             :                                 }
     751         214 :                                 state->buffer.length = 1;
     752             : 
     753         214 :                                 vector[0].iov_base = (void *)(state->buffer.data);
     754         214 :                                 vector[0].iov_len = 1;
     755         214 :                                 *_vector = vector;
     756         214 :                                 *_count = 1;
     757             :                         }
     758         232 :                         break;
     759        3310 :                 case HTTP_MORE_DATA_EXPECTED: {
     760        3310 :                         size_t toread = 1;
     761           0 :                         size_t total;
     762        3310 :                         if (state->parser_state == HTTP_READING_BODY ||
     763        3302 :                             state->parser_state == HTTP_READING_CHUNK) {
     764          40 :                                 struct http_request *resp = state->response;
     765          40 :                                 toread = resp->remaining_content_length -
     766          40 :                                          state->buffer.length;
     767             :                         }
     768             : 
     769        3310 :                         total = toread + state->buffer.length;
     770             : 
     771        3310 :                         if (total < state->buffer.length)  {
     772           0 :                                 DBG_ERR("adding %zu to buf len %zu "
     773             :                                         "will overflow\n",
     774             :                                         toread,
     775             :                                         state->buffer.length);
     776           0 :                                         return -1;
     777             :                         }
     778             : 
     779             :                         /*
     780             :                          * test if content-length message exceeds the
     781             :                          * specified max_content_length
     782             :                          * Note: This check won't be hit at the moment
     783             :                          *       due to an existing check in parse_headers
     784             :                          *       which will skip the body. Check is here
     785             :                          *       for completeness and to cater for future
     786             :                          *       code changes.
     787             :                          */
     788        3310 :                         if (state->parser_state == HTTP_READING_BODY) {
     789           8 :                                 if (total > state->max_content_length)  {
     790           0 :                                         DBG_ERR("content size %zu exceeds "
     791             :                                                 "max content len %"PRIu64"\n",
     792             :                                                 total,
     793             :                                                 state->max_content_length);
     794           0 :                                         return -1;
     795             :                                 }
     796             :                         }
     797             : 
     798        3310 :                         state->buffer.data =
     799        3310 :                                 talloc_realloc(state, state->buffer.data,
     800             :                                                uint8_t,
     801             :                                                state->buffer.length + toread);
     802        3310 :                         if (!state->buffer.data) {
     803           0 :                                 return -1;
     804             :                         }
     805        3310 :                         state->buffer.length += toread;
     806        3310 :                         vector[0].iov_base = (void *)(state->buffer.data +
     807        3310 :                                              state->buffer.length - toread);
     808        3310 :                         vector[0].iov_len = toread;
     809        3310 :                         *_vector = vector;
     810        3310 :                         *_count = 1;
     811        3310 :                         break;
     812             :                 }
     813           2 :                 case HTTP_DATA_CORRUPTED:
     814             :                 case HTTP_REQUEST_CANCELED:
     815             :                 case HTTP_DATA_TOO_LONG:
     816           2 :                         return -1;
     817           0 :                         break;
     818           0 :                 default:
     819           0 :                         DEBUG(0, ("%s: Unexpected status\n", __func__));
     820           0 :                         break;
     821             :         }
     822        3542 :         return 0;
     823             : }
     824             : 
     825             : 
     826             : /**
     827             :  * Reads a HTTP response
     828             :  */
     829             : static void http_read_response_done(struct tevent_req *);
     830          20 : struct tevent_req *http_read_response_send(TALLOC_CTX *mem_ctx,
     831             :                                            struct tevent_context *ev,
     832             :                                            struct http_conn *http_conn,
     833             :                                            size_t max_content_length)
     834             : {
     835           0 :         struct tevent_req               *req;
     836           0 :         struct tevent_req               *subreq;
     837           0 :         struct http_read_response_state *state;
     838             : 
     839          20 :         DEBUG(11, ("%s: Reading HTTP response\n", __func__));
     840             : 
     841             :         /* Sanity checks */
     842          20 :         if (ev == NULL || http_conn == NULL) {
     843           0 :                 DEBUG(0, ("%s: Invalid parameter\n", __func__));
     844           0 :                 return NULL;
     845             :         }
     846             : 
     847          20 :         req = tevent_req_create(mem_ctx, &state, struct http_read_response_state);
     848          20 :         if (req == NULL) {
     849           0 :                 return NULL;
     850             :         }
     851             : 
     852          20 :         state->max_headers_size = HTTP_MAX_HEADER_SIZE;
     853          20 :         state->max_content_length = (uint64_t)max_content_length;
     854          20 :         state->parser_state = HTTP_READING_FIRSTLINE;
     855          20 :         state->response = talloc_zero(state, struct http_request);
     856          20 :         if (tevent_req_nomem(state->response, req)) {
     857           0 :                 return tevent_req_post(req, ev);
     858             :         }
     859             : 
     860          20 :         subreq = tstream_readv_pdu_send(state, ev, http_conn->tstreams.active,
     861             :                                         http_read_response_next_vector,
     862             :                                         state);
     863          20 :         if (tevent_req_nomem(subreq,req)) {
     864           0 :                 return tevent_req_post(req, ev);
     865             :         }
     866          20 :         tevent_req_set_callback(subreq, http_read_response_done, req);
     867             : 
     868          20 :         return req;
     869             : }
     870             : 
     871          20 : static void http_read_response_done(struct tevent_req *subreq)
     872             : {
     873           0 :         NTSTATUS                        status;
     874           0 :         struct tevent_req               *req;
     875           0 :         enum http_body_type             ret;
     876           0 :         int                             sys_errno;
     877             : 
     878          20 :         if (!subreq) {
     879           0 :                 DEBUG(0, ("%s: Invalid parameter\n", __func__));
     880           2 :                 return;
     881             :         }
     882             : 
     883          20 :         req = tevent_req_callback_data(subreq, struct tevent_req);
     884             : 
     885          20 :         ret = tstream_readv_pdu_recv(subreq, &sys_errno);
     886          20 :         DEBUG(11, ("%s: HTTP response read (%d bytes)\n", __func__, ret));
     887          20 :         TALLOC_FREE(subreq);
     888          20 :         if (ret == -1) {
     889           2 :                 status = map_nt_error_from_unix_common(sys_errno);
     890           2 :                 DEBUG(0, ("%s: Failed to read HTTP response: %s\n",
     891             :                           __func__, nt_errstr(status)));
     892           2 :                 tevent_req_nterror(req, status);
     893           2 :                 return;
     894             :         }
     895             : 
     896          18 :         tevent_req_done(req);
     897             : }
     898             : 
     899          20 : NTSTATUS http_read_response_recv(struct tevent_req *req,
     900             :                                  TALLOC_CTX *mem_ctx,
     901             :                                  struct http_request **response)
     902             : {
     903           0 :         NTSTATUS status;
     904           0 :         struct http_read_response_state *state;
     905             : 
     906          20 :         if (!mem_ctx || !response || !req) {
     907           0 :                 DEBUG(0, ("%s: Invalid parameter\n", __func__));
     908           0 :                 return NT_STATUS_INVALID_PARAMETER;
     909             :         }
     910          20 :         if (tevent_req_is_nterror(req, &status)) {
     911           2 :                 tevent_req_received(req);
     912           2 :                 return status;
     913             :         }
     914             : 
     915          18 :         state = tevent_req_data(req, struct http_read_response_state);
     916          18 :         *response = state->response;
     917          18 :         talloc_steal(mem_ctx, state->response);
     918             : 
     919          18 :         tevent_req_received(req);
     920             : 
     921          18 :         return NT_STATUS_OK;
     922             : }
     923             : 
     924          20 : static const char *http_method_str(enum http_cmd_type type)
     925             : {
     926           0 :         const char *method;
     927             : 
     928          20 :         switch (type) {
     929          20 :         case HTTP_REQ_POST:
     930          20 :                 method = "POST";
     931          20 :                 break;
     932           0 :         case HTTP_REQ_RPC_IN_DATA:
     933           0 :                 method = "RPC_IN_DATA";
     934           0 :                 break;
     935           0 :         case HTTP_REQ_RPC_OUT_DATA:
     936           0 :                 method = "RPC_OUT_DATA";
     937           0 :                 break;
     938           0 :         default:
     939           0 :                 method = NULL;
     940           0 :                 break;
     941             :         }
     942             : 
     943          20 :         return method;
     944             : }
     945             : 
     946          20 : static NTSTATUS http_push_request_line(TALLOC_CTX *mem_ctx,
     947             :                                        DATA_BLOB *buffer,
     948             :                                        const struct http_request *req)
     949             : {
     950           0 :         const char      *method;
     951           0 :         char            *str;
     952             : 
     953             :         /* Sanity checks */
     954          20 :         if (!buffer || !req) {
     955           0 :                 DEBUG(0, ("%s: Invalid parameter\n", __func__));
     956           0 :                 return NT_STATUS_INVALID_PARAMETER;
     957             :         }
     958             : 
     959          20 :         method = http_method_str(req->type);
     960          20 :         if (method == NULL) {
     961           0 :                 return NT_STATUS_INVALID_PARAMETER;
     962             :         }
     963             : 
     964          20 :         str = talloc_asprintf(mem_ctx, "%s %s HTTP/%c.%c\r\n", method,
     965          20 :                               req->uri, req->major, req->minor);
     966          20 :         if (str == NULL)
     967           0 :                 return NT_STATUS_NO_MEMORY;
     968             : 
     969          20 :         if (!data_blob_append(mem_ctx, buffer, str, strlen(str))) {
     970           0 :                 talloc_free(str);
     971           0 :                 return NT_STATUS_NO_MEMORY;
     972             :         }
     973             : 
     974          20 :         talloc_free(str);
     975          20 :         return NT_STATUS_OK;
     976             : }
     977             : 
     978          20 : static NTSTATUS http_push_headers(TALLOC_CTX *mem_ctx,
     979             :                                   DATA_BLOB *blob,
     980             :                                   struct http_request *req)
     981             : {
     982          20 :         struct http_header      *header = NULL;
     983          20 :         char                    *header_str = NULL;
     984           0 :         size_t                  len;
     985             : 
     986             :         /* Sanity checks */
     987          20 :         if (!blob || !req) {
     988           0 :                 DEBUG(0, ("%s: Invalid parameter\n", __func__));
     989           0 :                 return NT_STATUS_INVALID_PARAMETER;
     990             :         }
     991             : 
     992          92 :         for (header = req->headers; header != NULL; header = header->next) {
     993          72 :                 header_str = talloc_asprintf(mem_ctx, "%s: %s\r\n",
     994             :                                              header->key, header->value);
     995          72 :                 if (header_str == NULL) {
     996           0 :                         return NT_STATUS_NO_MEMORY;
     997             :                 }
     998             : 
     999          72 :                 len = strlen(header_str);
    1000          72 :                 if (!data_blob_append(mem_ctx, blob, header_str, len)) {
    1001           0 :                         talloc_free(header_str);
    1002           0 :                         return NT_STATUS_NO_MEMORY;
    1003             :                 }
    1004          72 :                 talloc_free(header_str);
    1005             :         }
    1006             : 
    1007          20 :         if (!data_blob_append(mem_ctx, blob, "\r\n",2)) {
    1008           0 :                 return NT_STATUS_NO_MEMORY;
    1009             :         }
    1010             : 
    1011          20 :         return NT_STATUS_OK;
    1012             : }
    1013             : 
    1014             : 
    1015          20 : static NTSTATUS http_push_body(TALLOC_CTX *mem_ctx,
    1016             :                                DATA_BLOB *blob,
    1017             :                                struct http_request *req)
    1018             : {
    1019             :         /* Sanity checks */
    1020          20 :         if (!blob || !req) {
    1021           0 :                 DEBUG(0, ("%s: Invalid parameter\n", __func__));
    1022           0 :                 return NT_STATUS_INVALID_PARAMETER;
    1023             :         }
    1024             : 
    1025          20 :         if (req->body.length) {
    1026           6 :                 if (!data_blob_append(mem_ctx, blob, req->body.data,
    1027             :                                 req->body.length)) {
    1028           0 :                         return NT_STATUS_NO_MEMORY;
    1029             :                 }
    1030             :         }
    1031             : 
    1032          20 :         return NT_STATUS_OK;
    1033             : }
    1034             : 
    1035             : struct http_send_request_state {
    1036             :         struct tevent_context   *ev;
    1037             :         struct loadparm_context *lp_ctx;
    1038             :         struct cli_credentials  *credentials;
    1039             :         struct http_request     *request;
    1040             :         DATA_BLOB               buffer;
    1041             :         struct iovec            iov;
    1042             :         ssize_t                 nwritten;
    1043             :         int                     sys_errno;
    1044             : };
    1045             : 
    1046             : /**
    1047             :  * Sends and HTTP request
    1048             :  */
    1049             : static void http_send_request_done(struct tevent_req *);
    1050          20 : struct tevent_req *http_send_request_send(TALLOC_CTX *mem_ctx,
    1051             :                                           struct tevent_context *ev,
    1052             :                                           struct http_conn *http_conn,
    1053             :                                           struct http_request *request)
    1054             : {
    1055           0 :         struct tevent_req               *req;
    1056           0 :         struct tevent_req               *subreq;
    1057          20 :         struct http_send_request_state  *state = NULL;
    1058           0 :         NTSTATUS                        status;
    1059             : 
    1060          20 :         DEBUG(11, ("%s: Sending HTTP request\n", __func__));
    1061             : 
    1062             :         /* Sanity checks */
    1063          20 :         if (ev == NULL || request == NULL || http_conn == NULL) {
    1064           0 :                 DEBUG(0, ("%s: Invalid parameter\n", __func__));
    1065           0 :                 return NULL;
    1066             :         }
    1067             : 
    1068          20 :         req = tevent_req_create(mem_ctx, &state, struct http_send_request_state);
    1069          20 :         if (req == NULL) {
    1070           0 :                 return NULL;
    1071             :         }
    1072             : 
    1073          20 :         state->ev = ev;
    1074          20 :         state->request = request;
    1075             : 
    1076             :         /* Push the request line */
    1077          20 :         status = http_push_request_line(state, &state->buffer, state->request);
    1078          20 :         if (!NT_STATUS_IS_OK(status)) {
    1079           0 :                 tevent_req_nterror(req, status);
    1080           0 :                 return tevent_req_post(req, ev);
    1081             :         }
    1082             : 
    1083             :         /* Push the headers */
    1084          20 :         status = http_push_headers(mem_ctx, &state->buffer, request);
    1085          20 :         if (!NT_STATUS_IS_OK(status)) {
    1086           0 :                 tevent_req_nterror(req, status);
    1087           0 :                 return tevent_req_post(req, ev);
    1088             :         }
    1089             : 
    1090             :         /* Push the body */
    1091          20 :         status = http_push_body(mem_ctx, &state->buffer, request);
    1092          20 :         if (!NT_STATUS_IS_OK(status)) {
    1093           0 :                 tevent_req_nterror(req, status);
    1094           0 :                 return tevent_req_post(req, ev);
    1095             :         }
    1096             : 
    1097          20 :         state->iov.iov_base = (char *) state->buffer.data;
    1098          20 :         state->iov.iov_len = state->buffer.length;
    1099          20 :         subreq = tstream_writev_queue_send(state,
    1100             :                                            ev,
    1101             :                                            http_conn->tstreams.active,
    1102             :                                            http_conn->send_queue,
    1103          20 :                                            &state->iov, 1);
    1104          20 :         if (tevent_req_nomem(subreq, req)) {
    1105           0 :                 return tevent_req_post(req, ev);
    1106             :         }
    1107          20 :         tevent_req_set_callback(subreq, http_send_request_done, req);
    1108             : 
    1109          20 :         return req;
    1110             : }
    1111             : 
    1112          20 : static void http_send_request_done(struct tevent_req *subreq)
    1113             : {
    1114           0 :         NTSTATUS                        status;
    1115           0 :         struct tevent_req               *req;
    1116           0 :         struct http_send_request_state  *state;
    1117             : 
    1118          20 :         req = tevent_req_callback_data(subreq, struct tevent_req);
    1119          20 :         state = tevent_req_data(req, struct http_send_request_state);
    1120             : 
    1121          20 :         state->nwritten = tstream_writev_queue_recv(subreq, &state->sys_errno);
    1122          20 :         TALLOC_FREE(subreq);
    1123          20 :         if (state->nwritten == -1 && state->sys_errno != 0) {
    1124           0 :                 status = map_nt_error_from_unix_common(state->sys_errno);
    1125           0 :                 DEBUG(0, ("%s: Failed to send HTTP request: %s\n",
    1126             :                           __func__, nt_errstr(status)));
    1127           0 :                 tevent_req_nterror(req, status);
    1128           0 :                 return;
    1129             :         }
    1130             : 
    1131          20 :         tevent_req_done(req);
    1132             : }
    1133             : 
    1134          20 : NTSTATUS http_send_request_recv(struct tevent_req *req)
    1135             : {
    1136           0 :         NTSTATUS status;
    1137             : 
    1138          20 :         if (!req) {
    1139           0 :                 DEBUG(0, ("%s: Invalid parameter\n", __func__));
    1140           0 :                 return NT_STATUS_INVALID_PARAMETER;
    1141             :         }
    1142             : 
    1143          20 :         if (tevent_req_is_nterror(req, &status)) {
    1144           0 :                 tevent_req_received(req);
    1145           0 :                 return status;
    1146             :         }
    1147             : 
    1148          20 :         tevent_req_received(req);
    1149             : 
    1150          20 :         return NT_STATUS_OK;
    1151             : }

Generated by: LCOV version 1.14