Coverage Report

Created: 2026-01-16 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/libcli/smb/smb1cli_read.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   Copyright (C) Gregor Beck 2013
5
6
   This program is free software; you can redistribute it and/or modify
7
   it under the terms of the GNU General Public License as published by
8
   the Free Software Foundation; either version 3 of the License, or
9
   (at your option) any later version.
10
11
   This program is distributed in the hope that it will be useful,
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
   GNU General Public License for more details.
15
16
   You should have received a copy of the GNU General Public License
17
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
*/
19
20
#include "includes.h"
21
#include "system/network.h"
22
#include "lib/util/tevent_ntstatus.h"
23
#include "smb_common.h"
24
#include "smbXcli_base.h"
25
26
struct smb1cli_readx_state {
27
  uint32_t size;
28
  uint16_t vwv[12];
29
  uint32_t received;
30
  uint8_t *buf;
31
  bool out_valid;
32
};
33
34
static void smb1cli_readx_done(struct tevent_req *subreq);
35
36
/**
37
 * Send an asynchrounus SMB_COM_READ_ANDX request.
38
 * <a href="http://msdn.microsoft.com/en-us/library/ee441839.aspx">MS-CIFS 2.2.4.42.1</a>,
39
 * <a href="http://msdn.microsoft.com/en-us/library/ff470250.aspx">MS-SMB 2.2.4.2.1</a>
40
 * @see smb1cli_readx_recv()
41
 * @todo fix API (min/max size, timeout)
42
 *
43
 * @param[in] mem_ctx The memory context for the result.
44
 * @param[in] ev The event context to work on.
45
 * @param[in] conn The smb connection.
46
 * @param[in] timeout_msec If positive a timeout for the request.
47
 * @param[in] pid The process identifier
48
 * @param[in] tcon The smb tree connect.
49
 * @param[in] session The smb session.
50
 * @param[in] fnum The file id of the file the data should be read from.
51
 * @param[in] offset The offset in bytes from the begin of file where to start reading.
52
 * @param[in] size The number of bytes to read.
53
 *
54
 * @return a tevent_req or NULL
55
 */
56
struct tevent_req *smb1cli_readx_send(TALLOC_CTX *mem_ctx,
57
              struct tevent_context *ev,
58
              struct smbXcli_conn *conn,
59
              uint32_t timeout_msec,
60
              uint32_t pid,
61
              struct smbXcli_tcon *tcon,
62
              struct smbXcli_session *session,
63
              uint16_t fnum,
64
              uint64_t offset,
65
              uint32_t size)
66
0
{
67
0
  NTSTATUS status;
68
0
  struct tevent_req *req, *subreq;
69
0
  struct smb1cli_readx_state *state;
70
0
  uint8_t wct = 10;
71
72
0
  req = tevent_req_create(mem_ctx, &state, struct smb1cli_readx_state);
73
0
  if (req == NULL) {
74
0
    return NULL;
75
0
  }
76
0
  state->size = size;
77
78
0
  SCVAL(state->vwv + 0, 0, 0xFF);
79
0
  SCVAL(state->vwv + 0, 1, 0);
80
0
  SSVAL(state->vwv + 1, 0, 0);
81
0
  SSVAL(state->vwv + 2, 0, fnum);
82
0
  SIVAL(state->vwv + 3, 0, offset);
83
0
  SSVAL(state->vwv + 5, 0, size);
84
0
  SSVAL(state->vwv + 6, 0, size);
85
0
  SSVAL(state->vwv + 7, 0, (size >> 16));
86
0
  SSVAL(state->vwv + 8, 0, 0);
87
0
  SSVAL(state->vwv + 9, 0, 0);
88
89
0
  if (smb1cli_conn_capabilities(conn) & CAP_LARGE_FILES) {
90
0
    SIVAL(state->vwv + 10, 0,
91
0
          (((uint64_t)offset)>>32) & 0xffffffff);
92
0
    wct = 12;
93
0
  } else {
94
0
    if ((((uint64_t)offset) & 0xffffffff00000000LL) != 0) {
95
0
      DEBUG(10, ("smb1cli_readx_send got large offset where "
96
0
           "the server does not support it\n"));
97
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
98
0
      return tevent_req_post(req, ev);
99
0
    }
100
0
  }
101
102
0
  subreq = smb1cli_req_create(state, ev, conn, SMBreadX,
103
0
            0, 0, /* *_flags */
104
0
            0, 0, /* *_flags2 */
105
0
            timeout_msec, pid, tcon, session,
106
0
            wct, state->vwv,
107
0
            0, NULL);
108
0
  if (tevent_req_nomem(subreq, req)) {
109
0
    return tevent_req_post(req, ev);
110
0
  }
111
0
  tevent_req_set_callback(subreq, smb1cli_readx_done, req);
112
113
0
  status = smb1cli_req_chain_submit(&subreq, 1);
114
0
  if (tevent_req_nterror(req, status)) {
115
0
    return tevent_req_post(req, ev);
116
0
  }
117
118
0
  return req;
119
0
}
120
121
static void smb1cli_readx_done(struct tevent_req *subreq)
122
0
{
123
0
  struct tevent_req *req = tevent_req_callback_data(
124
0
    subreq, struct tevent_req);
125
0
  struct smb1cli_readx_state *state = tevent_req_data(
126
0
    req, struct smb1cli_readx_state);
127
0
  struct iovec *recv_iov = NULL;
128
0
  uint8_t wct;
129
0
  uint16_t *vwv;
130
0
  uint32_t num_bytes;
131
0
  uint8_t *bytes;
132
0
  uint16_t data_offset;
133
0
  uint32_t bytes_offset;
134
0
  NTSTATUS status;
135
0
  static const struct smb1cli_req_expected_response expected[] = {
136
0
  {
137
0
    .status = NT_STATUS_OK,
138
0
    .wct = 0x0C
139
0
  },
140
0
  {
141
0
    .status = STATUS_BUFFER_OVERFLOW,
142
0
    .wct = 0x0C
143
0
  },
144
0
  };
145
146
0
  status = smb1cli_req_recv(subreq, state,
147
0
          &recv_iov,
148
0
          NULL, /* phdr */
149
0
          &wct,
150
0
          &vwv,
151
0
          NULL, /* pvwv_offset */
152
0
          &num_bytes,
153
0
          &bytes,
154
0
          &bytes_offset,
155
0
          NULL, /* inbuf */
156
0
          expected, ARRAY_SIZE(expected));
157
0
  TALLOC_FREE(subreq);
158
0
  if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
159
    /* no error */
160
0
  } else {
161
0
    if (tevent_req_nterror(req, status)) {
162
0
      return;
163
0
    }
164
0
  }
165
166
  /* size is the number of bytes the server returned.
167
   * Might be zero. */
168
0
  state->received = SVAL(vwv + 5, 0);
169
0
  state->received |= (((unsigned int)SVAL(vwv + 7, 0)) << 16);
170
171
0
  if (state->received > state->size) {
172
0
    DEBUG(5,("server returned more than we wanted!\n"));
173
0
    tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
174
0
    return;
175
0
  }
176
177
  /*
178
   * bcc field must be valid for small reads, for large reads the 16-bit
179
   * bcc field can't be correct.
180
   */
181
0
  if ((state->received < 0xffff) && (state->received > num_bytes)) {
182
0
    DEBUG(5, ("server announced more bytes than sent\n"));
183
0
    tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
184
0
    return;
185
0
  }
186
187
0
  data_offset = SVAL(vwv+6, 0);
188
0
  if (data_offset < bytes_offset) {
189
0
    DEBUG(5, ("server returned invalid read&x data offset\n"));
190
0
    tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
191
0
    return;
192
0
  }
193
0
  if (smb_buffer_oob(num_bytes, data_offset - bytes_offset, state->received)) {
194
0
    DEBUG(5, ("server returned invalid read&x data offset\n"));
195
0
    tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
196
0
    return;
197
0
  }
198
199
0
  state->buf = bytes + (data_offset - bytes_offset);
200
201
0
  state->out_valid = true;
202
203
0
  if (tevent_req_nterror(req, status)) {
204
0
    return;
205
0
  }
206
207
0
  tevent_req_done(req);
208
0
}
209
210
/**
211
 * Receive the response to an asynchronous SMB_COM_READ_ANDX request.
212
 * <a href="http://msdn.microsoft.com/en-us/library/ee441872.aspx">MS-CIFS 2.2.4.42.2</a>,
213
 * <a href="http://msdn.microsoft.com/en-us/library/ff470017.aspx">MS-SMB 2.2.4.2.2</a>
214
 *
215
 * @warning rcvbuf is talloced from the request, so better make sure that you
216
 * copy it away before  you talloc_free(req). rcvbuf is NOT a talloc_ctx of its
217
 * own, so do not talloc_move it!
218
 *
219
 * @param[in] req A tevent request created with smb1cli_readx_send()
220
 * @param[out] received The number of bytes received.
221
 * @param[out] rcvbuf Pointer to the bytes received.
222
 *
223
 * @return NT_STATUS_OK or STATUS_BUFFER_OVERFLOW on success.
224
 */
225
NTSTATUS smb1cli_readx_recv(struct tevent_req *req,
226
          uint32_t *received,
227
          uint8_t **rcvbuf)
228
0
{
229
0
  struct smb1cli_readx_state *state = tevent_req_data(
230
0
    req, struct smb1cli_readx_state);
231
0
  NTSTATUS status = NT_STATUS_OK;
232
233
0
  if (tevent_req_is_nterror(req, &status) && !state->out_valid) {
234
0
    *received = 0;
235
0
    *rcvbuf = NULL;
236
0
    return status;
237
0
  }
238
0
  *received = state->received;
239
0
  *rcvbuf = state->buf;
240
0
  return status;
241
0
}