Coverage Report

Created: 2026-05-24 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/libcli/smb/smb2cli_notify.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   smb2 lib
4
   Copyright (C) Volker Lendecke 2017
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
#include "librpc/gen_ndr/ndr_notify.h"
26
27
struct smb2cli_notify_state {
28
  uint8_t fixed[32];
29
30
  struct iovec *recv_iov;
31
  uint8_t *data;
32
  uint32_t data_length;
33
34
  struct tevent_req *subreq;
35
  struct tevent_req *timeout_subreq;
36
  bool notify_async;
37
  bool report_pending;
38
};
39
40
static void smb2cli_notify_done(struct tevent_req *subreq);
41
static void smb2cli_notify_timedout(struct tevent_req *subreq);
42
static bool smb2cli_notify_cancel(struct tevent_req *req);
43
44
struct tevent_req *smb2cli_notify_send(TALLOC_CTX *mem_ctx,
45
               struct tevent_context *ev,
46
               struct smbXcli_conn *conn,
47
               uint32_t timeout_msec,
48
               struct smbXcli_session *session,
49
               struct smbXcli_tcon *tcon,
50
               uint32_t output_buffer_length,
51
               uint64_t fid_persistent,
52
               uint64_t fid_volatile,
53
               uint32_t completion_filter,
54
               bool recursive)
55
0
{
56
0
  struct tevent_req *req;
57
0
  struct smb2cli_notify_state *state;
58
0
  uint8_t *fixed;
59
0
  uint16_t watch_tree;
60
61
0
  req = tevent_req_create(mem_ctx, &state,
62
0
        struct smb2cli_notify_state);
63
0
  if (req == NULL) {
64
0
    return NULL;
65
0
  }
66
67
0
  watch_tree = recursive ? SMB2_WATCH_TREE : 0;
68
0
  fixed = state->fixed;
69
0
  SSVAL(fixed, 0, 32);
70
0
  SSVAL(fixed, 2, watch_tree);
71
0
  SIVAL(fixed, 4, output_buffer_length);
72
0
  SBVAL(fixed, 8, fid_persistent);
73
0
  SBVAL(fixed, 16, fid_volatile);
74
0
  SIVAL(fixed, 24, completion_filter);
75
0
  SIVAL(fixed, 28, 0);   /* reserved */
76
77
0
  state->subreq = smb2cli_req_send(state, ev, conn, SMB2_OP_NOTIFY,
78
0
           0, 0, /* flags */
79
0
           0, /* timeout_msec */
80
0
           tcon,
81
0
           session,
82
0
           state->fixed, sizeof(state->fixed),
83
0
           NULL, 0, /* dyn* */
84
0
           0); /* max_dyn_len */
85
0
  if (tevent_req_nomem(state->subreq, req)) {
86
0
    return tevent_req_post(req, ev);
87
0
  }
88
0
  tevent_req_set_callback(state->subreq, smb2cli_notify_done, req);
89
90
0
  if (timeout_msec != 0) {
91
0
    state->timeout_subreq = tevent_wakeup_send(
92
0
      state, ev, timeval_current_ofs_msec(timeout_msec));
93
0
    if (tevent_req_nomem(state->timeout_subreq, req)) {
94
0
      return tevent_req_post(req, ev);
95
0
    }
96
0
    tevent_req_set_callback(
97
0
      state->timeout_subreq, smb2cli_notify_timedout, req);
98
0
  }
99
100
0
  tevent_req_set_cancel_fn(req, smb2cli_notify_cancel);
101
102
0
  return req;
103
0
}
104
105
static bool smb2cli_notify_cancel(struct tevent_req *req)
106
0
{
107
0
  struct smb2cli_notify_state *state = tevent_req_data(
108
0
    req, struct smb2cli_notify_state);
109
0
  bool ok;
110
111
0
  TALLOC_FREE(state->timeout_subreq);
112
113
0
  ok = tevent_req_cancel(state->subreq);
114
0
  return ok;
115
0
}
116
117
static void smb2cli_notify_timedout(struct tevent_req *subreq)
118
0
{
119
0
  struct tevent_req *req = tevent_req_callback_data(
120
0
    subreq, struct tevent_req);
121
0
  struct smb2cli_notify_state *state = tevent_req_data(
122
0
    req, struct smb2cli_notify_state);
123
0
  bool ok;
124
125
0
  ok = tevent_wakeup_recv(subreq);
126
0
  if (!ok) {
127
0
    tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
128
0
    return;
129
0
  }
130
131
0
  ok = tevent_req_cancel(state->subreq);
132
0
  if (!ok) {
133
0
    tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
134
0
    return;
135
0
  }
136
0
}
137
138
void smb2cli_notify_set_notify_async(struct tevent_req *req)
139
0
{
140
0
  struct smb2cli_notify_state *state =
141
0
    tevent_req_data(req,
142
0
    struct smb2cli_notify_state);
143
144
0
  smb2cli_req_set_notify_async(state->subreq);
145
0
  state->notify_async = true;
146
0
}
147
148
static void smb2cli_notify_done(struct tevent_req *subreq)
149
0
{
150
0
  struct tevent_req *req = tevent_req_callback_data(
151
0
    subreq, struct tevent_req);
152
0
  struct smb2cli_notify_state *state = tevent_req_data(
153
0
    req, struct smb2cli_notify_state);
154
0
  NTSTATUS status;
155
0
  struct iovec *iov;
156
0
  uint16_t data_offset;
157
0
  static const struct smb2cli_req_expected_response expected[] = {
158
0
  {
159
0
    .status = NT_STATUS_OK,
160
0
    .body_size = 0x09
161
0
  }
162
0
  };
163
164
0
  SMB_ASSERT(state->subreq == subreq);
165
166
0
  status = smb2cli_req_recv(subreq, state, &iov,
167
0
          expected, ARRAY_SIZE(expected));
168
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_PENDING) && state->notify_async) {
169
0
    state->notify_async = false;
170
0
    state->report_pending = true;
171
0
    tevent_req_notify_callback(req);
172
0
    return;
173
0
  }
174
0
  state->notify_async = false;
175
0
  state->report_pending = false;
176
0
  state->subreq = NULL;
177
0
  TALLOC_FREE(subreq);
178
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) {
179
0
    status = NT_STATUS_IO_TIMEOUT;
180
0
  }
181
0
  if (tevent_req_nterror(req, status)) {
182
0
    return;
183
0
  }
184
185
0
  data_offset = SVAL(iov[1].iov_base, 2);
186
0
  state->data_length = IVAL(iov[1].iov_base, 4);
187
188
0
  if ((data_offset != SMB2_HDR_BODY + 8) ||
189
0
      (state->data_length > iov[2].iov_len)) {
190
0
    tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
191
0
    return;
192
0
  }
193
194
0
  state->recv_iov = iov;
195
0
  state->data = (uint8_t *)iov[2].iov_base;
196
0
  tevent_req_done(req);
197
0
}
198
199
NTSTATUS smb2cli_notify_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
200
           uint8_t **data, uint32_t *data_length)
201
0
{
202
0
  struct smb2cli_notify_state *state = tevent_req_data(
203
0
    req, struct smb2cli_notify_state);
204
0
  NTSTATUS status;
205
206
0
  if (state->report_pending) {
207
0
    *data_length = 0;
208
0
    *data = NULL;
209
0
    return NT_STATUS_PENDING;
210
0
  }
211
212
0
  if (tevent_req_is_nterror(req, &status)) {
213
0
    return status;
214
0
  }
215
0
  talloc_steal(mem_ctx, state->recv_iov);
216
0
  *data_length = state->data_length;
217
0
  *data = state->data;
218
0
  return NT_STATUS_OK;
219
0
}
220
221
NTSTATUS smb2cli_notify(struct smbXcli_conn *conn,
222
      uint32_t timeout_msec,
223
      struct smbXcli_session *session,
224
      struct smbXcli_tcon *tcon,
225
      uint32_t output_buffer_length,
226
      uint64_t fid_persistent,
227
      uint64_t fid_volatile,
228
      uint32_t completion_filter,
229
      bool recursive,
230
      TALLOC_CTX *mem_ctx,
231
      uint8_t **data,
232
      uint32_t *data_length)
233
0
{
234
0
  TALLOC_CTX *frame = talloc_stackframe();
235
0
  struct tevent_context *ev;
236
0
  struct tevent_req *req;
237
0
  NTSTATUS status = NT_STATUS_NO_MEMORY;
238
239
0
  if (smbXcli_conn_has_async_calls(conn)) {
240
    /*
241
     * Can't use sync call while an async call is in flight
242
     */
243
0
    status = NT_STATUS_INVALID_PARAMETER;
244
0
    goto fail;
245
0
  }
246
0
  ev = samba_tevent_context_init(frame);
247
0
  if (ev == NULL) {
248
0
    goto fail;
249
0
  }
250
0
  req = smb2cli_notify_send(frame, ev, conn, timeout_msec,
251
0
          session, tcon, output_buffer_length,
252
0
          fid_persistent, fid_volatile,
253
0
          completion_filter, recursive);
254
0
  if (req == NULL) {
255
0
    goto fail;
256
0
  }
257
0
  if (!tevent_req_poll_ntstatus(req, ev, &status)) {
258
0
    goto fail;
259
0
  }
260
0
  status = smb2cli_notify_recv(req, mem_ctx, data, data_length);
261
0
 fail:
262
  TALLOC_FREE(frame);
263
0
  return status;
264
0
}