/src/samba/libcli/nbt/namerefresh.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | send out a name refresh request |
5 | | |
6 | | Copyright (C) Andrew Tridgell 2005 |
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 <tevent.h> |
24 | | #include "../libcli/nbt/libnbt.h" |
25 | | #include "../libcli/nbt/nbt_proto.h" |
26 | | #include "lib/socket/socket.h" |
27 | | #include "lib/util/tevent_ntstatus.h" |
28 | | |
29 | | /* |
30 | | send a nbt name refresh request |
31 | | */ |
32 | | struct nbt_name_request *nbt_name_refresh_send(struct nbt_name_socket *nbtsock, |
33 | | struct nbt_name_refresh *io) |
34 | 0 | { |
35 | 0 | struct nbt_name_request *req; |
36 | 0 | struct nbt_name_packet *packet; |
37 | 0 | struct socket_address *dest; |
38 | |
|
39 | 0 | packet = talloc_zero(nbtsock, struct nbt_name_packet); |
40 | 0 | if (packet == NULL) return NULL; |
41 | | |
42 | 0 | packet->qdcount = 1; |
43 | 0 | packet->arcount = 1; |
44 | 0 | packet->operation = NBT_OPCODE_REFRESH; |
45 | 0 | if (io->in.broadcast) { |
46 | 0 | packet->operation |= NBT_FLAG_BROADCAST; |
47 | 0 | } |
48 | |
|
49 | 0 | packet->questions = talloc_array(packet, struct nbt_name_question, 1); |
50 | 0 | if (packet->questions == NULL) goto failed; |
51 | | |
52 | 0 | packet->questions[0].name = io->in.name; |
53 | 0 | packet->questions[0].question_type = NBT_QTYPE_NETBIOS; |
54 | 0 | packet->questions[0].question_class = NBT_QCLASS_IP; |
55 | |
|
56 | 0 | packet->additional = talloc_array(packet, struct nbt_res_rec, 1); |
57 | 0 | if (packet->additional == NULL) goto failed; |
58 | | |
59 | 0 | packet->additional[0].name = io->in.name; |
60 | 0 | packet->additional[0].rr_type = NBT_QTYPE_NETBIOS; |
61 | 0 | packet->additional[0].rr_class = NBT_QCLASS_IP; |
62 | 0 | packet->additional[0].ttl = io->in.ttl; |
63 | 0 | packet->additional[0].rdata.netbios.length = 6; |
64 | 0 | packet->additional[0].rdata.netbios.addresses = talloc_array(packet->additional, |
65 | 0 | struct nbt_rdata_address, 1); |
66 | 0 | if (packet->additional[0].rdata.netbios.addresses == NULL) goto failed; |
67 | 0 | packet->additional[0].rdata.netbios.addresses[0].nb_flags = io->in.nb_flags; |
68 | 0 | packet->additional[0].rdata.netbios.addresses[0].ipaddr = |
69 | 0 | talloc_strdup(packet->additional, io->in.address); |
70 | |
|
71 | 0 | dest = socket_address_from_strings(nbtsock, |
72 | 0 | nbtsock->sock->backend_name, |
73 | 0 | io->in.dest_addr, io->in.dest_port); |
74 | 0 | if (dest == NULL) goto failed; |
75 | 0 | req = nbt_name_request_send(nbtsock, nbtsock, dest, packet, |
76 | 0 | io->in.timeout, io->in.retries, false); |
77 | 0 | if (req == NULL) goto failed; |
78 | | |
79 | 0 | talloc_free(packet); |
80 | 0 | return req; |
81 | | |
82 | 0 | failed: |
83 | 0 | talloc_free(packet); |
84 | 0 | return NULL; |
85 | 0 | } |
86 | | |
87 | | /* |
88 | | wait for a refresh reply |
89 | | */ |
90 | | _PUBLIC_ NTSTATUS nbt_name_refresh_recv(struct nbt_name_request *req, |
91 | | TALLOC_CTX *mem_ctx, struct nbt_name_refresh *io) |
92 | 0 | { |
93 | 0 | NTSTATUS status; |
94 | 0 | struct nbt_name_packet *packet; |
95 | |
|
96 | 0 | status = nbt_name_request_recv(req); |
97 | 0 | if (!NT_STATUS_IS_OK(status) || |
98 | 0 | req->num_replies == 0) { |
99 | 0 | talloc_free(req); |
100 | 0 | return status; |
101 | 0 | } |
102 | | |
103 | 0 | packet = req->replies[0].packet; |
104 | 0 | io->out.reply_from = talloc_steal(mem_ctx, req->replies[0].dest->addr); |
105 | |
|
106 | 0 | if (packet->ancount != 1 || |
107 | 0 | packet->answers[0].rr_type != NBT_QTYPE_NETBIOS || |
108 | 0 | packet->answers[0].rr_class != NBT_QCLASS_IP) { |
109 | 0 | talloc_free(req); |
110 | 0 | return NT_STATUS_INVALID_NETWORK_RESPONSE; |
111 | 0 | } |
112 | | |
113 | 0 | io->out.rcode = packet->operation & NBT_RCODE; |
114 | 0 | io->out.name = packet->answers[0].name; |
115 | 0 | if (packet->answers[0].rdata.netbios.length < 6) { |
116 | 0 | talloc_free(req); |
117 | 0 | return NT_STATUS_INVALID_NETWORK_RESPONSE; |
118 | 0 | } |
119 | 0 | io->out.reply_addr = talloc_steal(mem_ctx, |
120 | 0 | packet->answers[0].rdata.netbios.addresses[0].ipaddr); |
121 | 0 | talloc_steal(mem_ctx, io->out.name.name); |
122 | 0 | talloc_steal(mem_ctx, io->out.name.scope); |
123 | |
|
124 | 0 | talloc_free(req); |
125 | |
|
126 | 0 | return NT_STATUS_OK; |
127 | 0 | } |
128 | | |
129 | | /* |
130 | | synchronous name refresh request |
131 | | */ |
132 | | _PUBLIC_ NTSTATUS nbt_name_refresh(struct nbt_name_socket *nbtsock, |
133 | | TALLOC_CTX *mem_ctx, struct nbt_name_refresh *io) |
134 | 0 | { |
135 | 0 | struct nbt_name_request *req = nbt_name_refresh_send(nbtsock, io); |
136 | 0 | return nbt_name_refresh_recv(req, mem_ctx, io); |
137 | 0 | } |
138 | | |
139 | | |
140 | | |
141 | | /** |
142 | | a wins name refresh with multiple WINS servers and multiple |
143 | | addresses to refresh. Try each WINS server in turn, until we get a |
144 | | reply for each address |
145 | | */ |
146 | | struct nbt_name_refresh_wins_state { |
147 | | struct nbt_name_socket *nbtsock; |
148 | | struct nbt_name_refresh *io; |
149 | | char **wins_servers; |
150 | | uint16_t wins_port; |
151 | | char **addresses; |
152 | | int address_idx; |
153 | | }; |
154 | | |
155 | | static void nbt_name_refresh_wins_handler(struct nbt_name_request *subreq); |
156 | | |
157 | | /** |
158 | | the async send call for a multi-server WINS refresh |
159 | | */ |
160 | | _PUBLIC_ struct tevent_req *nbt_name_refresh_wins_send(TALLOC_CTX *mem_ctx, |
161 | | struct tevent_context *ev, |
162 | | struct nbt_name_socket *nbtsock, |
163 | | struct nbt_name_refresh_wins *io) |
164 | 0 | { |
165 | 0 | struct tevent_req *req; |
166 | 0 | struct nbt_name_refresh_wins_state *state; |
167 | 0 | struct nbt_name_request *subreq; |
168 | |
|
169 | 0 | req = tevent_req_create(mem_ctx, &state, |
170 | 0 | struct nbt_name_refresh_wins_state); |
171 | 0 | if (req == NULL) { |
172 | 0 | return NULL; |
173 | 0 | } |
174 | | |
175 | 0 | state->io = talloc(state, struct nbt_name_refresh); |
176 | 0 | if (tevent_req_nomem(state->io, req)) { |
177 | 0 | return tevent_req_post(req, ev); |
178 | 0 | } |
179 | | |
180 | 0 | if (io->in.wins_servers == NULL) { |
181 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
182 | 0 | return tevent_req_post(req, ev); |
183 | 0 | } |
184 | | |
185 | 0 | if (io->in.wins_servers[0] == NULL) { |
186 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
187 | 0 | return tevent_req_post(req, ev); |
188 | 0 | } |
189 | | |
190 | 0 | if (io->in.addresses == NULL) { |
191 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
192 | 0 | return tevent_req_post(req, ev); |
193 | 0 | } |
194 | | |
195 | 0 | if (io->in.addresses[0] == NULL) { |
196 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
197 | 0 | return tevent_req_post(req, ev); |
198 | 0 | } |
199 | | |
200 | 0 | state->wins_port = io->in.wins_port; |
201 | 0 | state->wins_servers = str_list_copy(state, io->in.wins_servers); |
202 | 0 | if (tevent_req_nomem(state->wins_servers, req)) { |
203 | 0 | return tevent_req_post(req, ev); |
204 | 0 | } |
205 | | |
206 | 0 | state->addresses = str_list_copy(state, io->in.addresses); |
207 | 0 | if (tevent_req_nomem(state->addresses, req)) { |
208 | 0 | return tevent_req_post(req, ev); |
209 | 0 | } |
210 | | |
211 | 0 | state->io->in.name = io->in.name; |
212 | 0 | state->io->in.dest_addr = state->wins_servers[0]; |
213 | 0 | state->io->in.dest_port = state->wins_port; |
214 | 0 | state->io->in.address = io->in.addresses[0]; |
215 | 0 | state->io->in.nb_flags = io->in.nb_flags; |
216 | 0 | state->io->in.broadcast = false; |
217 | 0 | state->io->in.ttl = io->in.ttl; |
218 | 0 | state->io->in.timeout = 2; |
219 | 0 | state->io->in.retries = 2; |
220 | |
|
221 | 0 | state->nbtsock = nbtsock; |
222 | 0 | state->address_idx = 0; |
223 | |
|
224 | 0 | subreq = nbt_name_refresh_send(nbtsock, state->io); |
225 | 0 | if (tevent_req_nomem(subreq, req)) { |
226 | 0 | return tevent_req_post(req, ev); |
227 | 0 | } |
228 | | |
229 | 0 | subreq->async.fn = nbt_name_refresh_wins_handler; |
230 | 0 | subreq->async.private_data = req; |
231 | |
|
232 | 0 | return req; |
233 | 0 | } |
234 | | |
235 | | static void nbt_name_refresh_wins_handler(struct nbt_name_request *subreq) |
236 | 0 | { |
237 | 0 | struct tevent_req *req = |
238 | 0 | talloc_get_type_abort(subreq->async.private_data, |
239 | 0 | struct tevent_req); |
240 | 0 | struct nbt_name_refresh_wins_state *state = |
241 | 0 | tevent_req_data(req, |
242 | 0 | struct nbt_name_refresh_wins_state); |
243 | 0 | NTSTATUS status; |
244 | |
|
245 | 0 | status = nbt_name_refresh_recv(subreq, state, state->io); |
246 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { |
247 | | /* the refresh timed out - try the next WINS server */ |
248 | 0 | state->wins_servers++; |
249 | 0 | if (state->wins_servers[0] == NULL) { |
250 | 0 | tevent_req_nterror(req, status); |
251 | 0 | return; |
252 | 0 | } |
253 | | |
254 | 0 | state->address_idx = 0; |
255 | 0 | state->io->in.dest_addr = state->wins_servers[0]; |
256 | 0 | state->io->in.dest_port = state->wins_port; |
257 | 0 | state->io->in.address = state->addresses[0]; |
258 | |
|
259 | 0 | subreq = nbt_name_refresh_send(state->nbtsock, state->io); |
260 | 0 | if (tevent_req_nomem(subreq, req)) { |
261 | 0 | return; |
262 | 0 | } |
263 | 0 | subreq->async.fn = nbt_name_refresh_wins_handler; |
264 | 0 | subreq->async.private_data = req; |
265 | 0 | } else if (!NT_STATUS_IS_OK(status)) { |
266 | 0 | tevent_req_nterror(req, status); |
267 | 0 | return; |
268 | 0 | } |
269 | | |
270 | 0 | if (state->io->out.rcode == 0 && |
271 | 0 | state->addresses[state->address_idx+1] != NULL) { |
272 | | /* refresh our next address */ |
273 | 0 | state->io->in.address = state->addresses[++(state->address_idx)]; |
274 | 0 | subreq = nbt_name_refresh_send(state->nbtsock, state->io); |
275 | 0 | if (tevent_req_nomem(subreq, req)) { |
276 | 0 | return; |
277 | 0 | } |
278 | 0 | subreq->async.fn = nbt_name_refresh_wins_handler; |
279 | 0 | subreq->async.private_data = req; |
280 | 0 | return; |
281 | 0 | } |
282 | | |
283 | 0 | tevent_req_done(req); |
284 | 0 | } |
285 | | |
286 | | /* |
287 | | multi-homed WINS name refresh - recv side |
288 | | */ |
289 | | _PUBLIC_ NTSTATUS nbt_name_refresh_wins_recv(struct tevent_req *req, |
290 | | TALLOC_CTX *mem_ctx, |
291 | | struct nbt_name_refresh_wins *io) |
292 | 0 | { |
293 | 0 | struct nbt_name_refresh_wins_state *state = |
294 | 0 | tevent_req_data(req, |
295 | 0 | struct nbt_name_refresh_wins_state); |
296 | 0 | NTSTATUS status; |
297 | |
|
298 | 0 | if (tevent_req_is_nterror(req, &status)) { |
299 | 0 | tevent_req_received(req); |
300 | 0 | return status; |
301 | 0 | } |
302 | | |
303 | 0 | io->out.wins_server = talloc_move(mem_ctx, &state->wins_servers[0]); |
304 | 0 | io->out.rcode = state->io->out.rcode; |
305 | |
|
306 | 0 | tevent_req_received(req); |
307 | 0 | return NT_STATUS_OK; |
308 | 0 | } |
309 | | |
310 | | /* |
311 | | multi-homed WINS refresh - sync interface |
312 | | */ |
313 | | _PUBLIC_ NTSTATUS nbt_name_refresh_wins(struct nbt_name_socket *nbtsock, |
314 | | TALLOC_CTX *mem_ctx, |
315 | | struct nbt_name_refresh_wins *io) |
316 | 0 | { |
317 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
318 | 0 | struct tevent_context *ev; |
319 | 0 | struct tevent_req *subreq; |
320 | 0 | NTSTATUS status; |
321 | | |
322 | | /* |
323 | | * TODO: create a temporary event context |
324 | | */ |
325 | 0 | ev = nbtsock->event_ctx; |
326 | |
|
327 | 0 | subreq = nbt_name_refresh_wins_send(frame, ev, nbtsock, io); |
328 | 0 | if (subreq == NULL) { |
329 | 0 | talloc_free(frame); |
330 | 0 | return NT_STATUS_NO_MEMORY; |
331 | 0 | } |
332 | | |
333 | 0 | if (!tevent_req_poll(subreq, ev)) { |
334 | 0 | status = map_nt_error_from_unix_common(errno); |
335 | 0 | talloc_free(frame); |
336 | 0 | return status; |
337 | 0 | } |
338 | | |
339 | 0 | status = nbt_name_refresh_wins_recv(subreq, mem_ctx, io); |
340 | 0 | if (!NT_STATUS_IS_OK(status)) { |
341 | 0 | talloc_free(frame); |
342 | 0 | return status; |
343 | 0 | } |
344 | | |
345 | 0 | TALLOC_FREE(frame); |
346 | 0 | return NT_STATUS_OK; |
347 | 0 | } |