Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : NBT client - used to lookup netbios names
5 :
6 : Copyright (C) Andrew Tridgell 1994-2005
7 : Copyright (C) Jelmer Vernooij 2003 (Conversion to popt)
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 :
22 : */
23 :
24 : #include "includes.h"
25 : #include "lib/cmdline/cmdline.h"
26 : #include "lib/socket/socket.h"
27 : #include "lib/events/events.h"
28 : #include "system/network.h"
29 : #include "system/locale.h"
30 : #include "lib/socket/netif.h"
31 : #include "librpc/gen_ndr/nbt.h"
32 : #include "../libcli/nbt/libnbt.h"
33 : #include "param/param.h"
34 :
35 : #include <string.h>
36 :
37 : #define MAX_NETBIOSNAME_LEN 16
38 :
39 : /* command line options */
40 : static struct {
41 : const char *broadcast_address;
42 : const char *unicast_address;
43 : bool find_master;
44 : bool wins_lookup;
45 : bool node_status;
46 : bool root_port;
47 : bool lookup_by_ip;
48 : bool case_sensitive;
49 : } options;
50 :
51 : /*
52 : clean any binary from a node name
53 : */
54 0 : static const char *clean_name(TALLOC_CTX *mem_ctx, const char *name)
55 : {
56 0 : char *ret = talloc_strdup(mem_ctx, name);
57 0 : int i;
58 0 : for (i=0;ret[i];i++) {
59 0 : if (!isprint((unsigned char)ret[i])) ret[i] = '.';
60 : }
61 0 : return ret;
62 : }
63 :
64 : /*
65 : turn a node status flags field into a string
66 : */
67 0 : static char *node_status_flags(TALLOC_CTX *mem_ctx, uint16_t flags)
68 : {
69 0 : char *ret;
70 0 : const char *group = " ";
71 0 : const char *type = "B";
72 :
73 0 : if (flags & NBT_NM_GROUP) {
74 0 : group = "<GROUP>";
75 : }
76 :
77 0 : switch (flags & NBT_NM_OWNER_TYPE) {
78 0 : case NBT_NODE_B:
79 0 : type = "B";
80 0 : break;
81 0 : case NBT_NODE_P:
82 0 : type = "P";
83 0 : break;
84 0 : case NBT_NODE_M:
85 0 : type = "M";
86 0 : break;
87 0 : case NBT_NODE_H:
88 0 : type = "H";
89 0 : break;
90 : }
91 :
92 0 : ret = talloc_asprintf(mem_ctx, "%s %s", group, type);
93 :
94 0 : if (flags & NBT_NM_DEREGISTER) {
95 0 : ret = talloc_asprintf_append_buffer(ret, " <DEREGISTERING>");
96 : }
97 0 : if (flags & NBT_NM_CONFLICT) {
98 0 : ret = talloc_asprintf_append_buffer(ret, " <CONFLICT>");
99 : }
100 0 : if (flags & NBT_NM_ACTIVE) {
101 0 : ret = talloc_asprintf_append_buffer(ret, " <ACTIVE>");
102 : }
103 0 : if (flags & NBT_NM_PERMANENT) {
104 0 : ret = talloc_asprintf_append_buffer(ret, " <PERMANENT>");
105 : }
106 :
107 0 : return ret;
108 : }
109 :
110 : /* do a single node status */
111 0 : static bool do_node_status(struct nbt_name_socket *nbtsock,
112 : const char *addr, uint16_t port)
113 : {
114 0 : struct nbt_name_status io;
115 0 : NTSTATUS status;
116 :
117 0 : io.in.name.name = "*";
118 0 : io.in.name.type = NBT_NAME_CLIENT;
119 0 : io.in.name.scope = NULL;
120 0 : io.in.dest_addr = addr;
121 0 : io.in.dest_port = port;
122 0 : io.in.timeout = 1;
123 0 : io.in.retries = 2;
124 :
125 0 : status = nbt_name_status(nbtsock, nbtsock, &io);
126 0 : if (NT_STATUS_IS_OK(status)) {
127 0 : int i;
128 0 : printf("Node status reply from %s\n",
129 : io.out.reply_from);
130 0 : for (i=0;i<io.out.status.num_names;i++) {
131 0 : d_printf("\t%-16s <%02x> %s\n",
132 0 : clean_name(nbtsock, io.out.status.names[i].name),
133 0 : io.out.status.names[i].type,
134 0 : node_status_flags(nbtsock, io.out.status.names[i].nb_flags));
135 : }
136 0 : printf("\n\tMAC Address = %02X-%02X-%02X-%02X-%02X-%02X\n",
137 0 : io.out.status.statistics.unit_id[0],
138 0 : io.out.status.statistics.unit_id[1],
139 0 : io.out.status.statistics.unit_id[2],
140 0 : io.out.status.statistics.unit_id[3],
141 0 : io.out.status.statistics.unit_id[4],
142 0 : io.out.status.statistics.unit_id[5]);
143 0 : return true;
144 : }
145 :
146 0 : return false;
147 : }
148 :
149 : /* do a single node query */
150 916 : static NTSTATUS do_node_query(struct nbt_name_socket *nbtsock,
151 : const char *addr,
152 : uint16_t port,
153 : const char *node_name,
154 : enum nbt_name_type node_type,
155 : bool broadcast)
156 : {
157 28 : struct nbt_name_query io;
158 28 : NTSTATUS status;
159 28 : int i;
160 :
161 916 : io.in.name.name = node_name;
162 916 : io.in.name.type = node_type;
163 916 : io.in.name.scope = NULL;
164 916 : io.in.dest_addr = addr;
165 916 : io.in.dest_port = port;
166 916 : io.in.broadcast = broadcast;
167 916 : io.in.wins_lookup = options.wins_lookup;
168 916 : io.in.timeout = 1;
169 916 : io.in.retries = 2;
170 :
171 916 : status = nbt_name_query(nbtsock, nbtsock, &io);
172 916 : NT_STATUS_NOT_OK_RETURN(status);
173 :
174 1572 : for (i=0;i<io.out.num_addrs;i++) {
175 786 : printf("%s %s<%02x>\n",
176 786 : io.out.reply_addrs[i],
177 : io.out.name.name,
178 786 : io.out.name.type);
179 : }
180 786 : if (options.node_status && io.out.num_addrs > 0) {
181 0 : do_node_status(nbtsock, io.out.reply_addrs[0], port);
182 : }
183 :
184 786 : return status;
185 : }
186 :
187 :
188 851 : static bool process_one(struct loadparm_context *lp_ctx, struct tevent_context *ev,
189 : struct interface *ifaces, const char *name, int nbt_port)
190 : {
191 851 : TALLOC_CTX *tmp_ctx = talloc_new(NULL);
192 851 : enum nbt_name_type node_type = NBT_NAME_CLIENT;
193 26 : char *node_name, *p;
194 26 : struct socket_address *all_zero_addr;
195 26 : struct nbt_name_socket *nbtsock;
196 851 : NTSTATUS status = NT_STATUS_OK;
197 26 : size_t nbt_len;
198 851 : bool ret = true;
199 :
200 851 : if (!options.case_sensitive) {
201 851 : name = strupper_talloc(tmp_ctx, name);
202 : }
203 :
204 851 : if (options.find_master) {
205 0 : node_type = NBT_NAME_MASTER;
206 0 : if (*name == '-' || *name == '_') {
207 0 : name = "\01\02__MSBROWSE__\02";
208 0 : node_type = NBT_NAME_MS;
209 : }
210 : }
211 :
212 851 : p = strchr(name, '#');
213 851 : if (p) {
214 0 : node_name = talloc_strndup(tmp_ctx, name, PTR_DIFF(p,name));
215 0 : node_type = (enum nbt_name_type)strtol(p+1, NULL, 16);
216 : } else {
217 851 : node_name = talloc_strdup(tmp_ctx, name);
218 : }
219 :
220 851 : nbt_len = strlen(node_name);
221 851 : if (nbt_len > MAX_NETBIOSNAME_LEN - 1) {
222 0 : printf("The specified netbios name [%s] is too long.\n",
223 : node_name);
224 0 : talloc_free(tmp_ctx);
225 0 : return false;
226 : }
227 :
228 851 : nbtsock = nbt_name_socket_init(tmp_ctx, ev);
229 :
230 851 : if (options.root_port) {
231 0 : all_zero_addr = socket_address_from_strings(tmp_ctx, nbtsock->sock->backend_name,
232 : "0.0.0.0", NBT_NAME_SERVICE_PORT);
233 :
234 0 : if (!all_zero_addr) {
235 0 : talloc_free(tmp_ctx);
236 0 : return false;
237 : }
238 :
239 0 : status = socket_listen(nbtsock->sock, all_zero_addr, 0, 0);
240 0 : if (!NT_STATUS_IS_OK(status)) {
241 0 : printf("Failed to bind to local port 137 - %s\n", nt_errstr(status));
242 0 : talloc_free(tmp_ctx);
243 0 : return false;
244 : }
245 : }
246 :
247 851 : if (options.lookup_by_ip) {
248 0 : ret = do_node_status(nbtsock, name, nbt_port);
249 0 : talloc_free(tmp_ctx);
250 0 : return ret;
251 : }
252 :
253 851 : if (options.broadcast_address) {
254 0 : status = do_node_query(nbtsock, options.broadcast_address, nbt_port,
255 : node_name, node_type, true);
256 851 : } else if (options.unicast_address) {
257 393 : status = do_node_query(nbtsock, options.unicast_address,
258 : nbt_port, node_name, node_type, false);
259 : } else {
260 14 : int i, num_interfaces;
261 :
262 458 : num_interfaces = iface_list_count(ifaces);
263 602 : for (i=0;i<num_interfaces;i++) {
264 523 : const char *bcast = iface_list_n_bcast(ifaces, i);
265 523 : if (bcast == NULL) continue;
266 523 : status = do_node_query(nbtsock, bcast, nbt_port,
267 : node_name, node_type, true);
268 523 : if (NT_STATUS_IS_OK(status)) break;
269 : }
270 : }
271 :
272 851 : if (!NT_STATUS_IS_OK(status)) {
273 65 : printf("Lookup failed - %s\n", nt_errstr(status));
274 65 : ret = false;
275 : }
276 :
277 851 : talloc_free(tmp_ctx);
278 851 : return ret;
279 : }
280 :
281 : /*
282 : main program
283 : */
284 851 : int main(int argc, const char *argv[])
285 : {
286 851 : bool ret = true;
287 26 : struct interface *ifaces;
288 26 : struct tevent_context *ev;
289 26 : poptContext pc;
290 26 : int opt;
291 851 : struct loadparm_context *lp_ctx = NULL;
292 851 : TALLOC_CTX *mem_ctx = NULL;
293 26 : bool ok;
294 26 : enum {
295 : OPT_BROADCAST_ADDRESS = 1000,
296 : OPT_UNICAST_ADDRESS,
297 : OPT_FIND_MASTER,
298 : OPT_WINS_LOOKUP,
299 : OPT_NODE_STATUS,
300 : OPT_ROOT_PORT,
301 : OPT_LOOKUP_BY_IP,
302 : OPT_CASE_SENSITIVE
303 : };
304 2553 : struct poptOption long_options[] = {
305 : POPT_AUTOHELP
306 : {
307 : .longName = "broadcast",
308 : .shortName = 'B',
309 : .argInfo = POPT_ARG_STRING,
310 : .arg = NULL,
311 : .val = OPT_BROADCAST_ADDRESS,
312 : .descrip = "Specify address to use for broadcasts",
313 : .argDescrip = "BROADCAST-ADDRESS"
314 : },
315 : {
316 : .longName = "unicast",
317 : .shortName = 'U',
318 : .argInfo = POPT_ARG_STRING,
319 : .arg = NULL,
320 : .val = OPT_UNICAST_ADDRESS,
321 : .descrip = "Specify address to use for unicast",
322 : .argDescrip = NULL
323 : },
324 : {
325 : .longName = "master-browser",
326 : .shortName = 'M',
327 : .argInfo = POPT_ARG_NONE,
328 : .arg = NULL,
329 : .val = OPT_FIND_MASTER,
330 : .descrip = "Search for a master browser",
331 : .argDescrip = NULL
332 : },
333 : {
334 : .longName = "wins",
335 : .shortName = 'W',
336 : .argInfo = POPT_ARG_NONE,
337 : .arg = NULL,
338 : .val = OPT_WINS_LOOKUP,
339 : .descrip = "Do a WINS lookup",
340 : .argDescrip = NULL
341 : },
342 : {
343 : .longName = "status",
344 : .shortName = 'S',
345 : .argInfo = POPT_ARG_NONE,
346 : .arg = NULL,
347 : .val = OPT_NODE_STATUS,
348 : .descrip = "Lookup node status as well",
349 : .argDescrip = NULL
350 : },
351 : {
352 : .longName = "root-port",
353 : .shortName = 'r',
354 : .argInfo = POPT_ARG_NONE,
355 : .arg = NULL,
356 : .val = OPT_ROOT_PORT,
357 : .descrip = "Use root port 137 (Win95 only replies to this)",
358 : .argDescrip = NULL
359 : },
360 : {
361 : .longName = "lookup-by-ip",
362 : .shortName = 'A',
363 : .argInfo = POPT_ARG_NONE,
364 : .arg = NULL,
365 : .val = OPT_LOOKUP_BY_IP,
366 : .descrip = "Do a node status on <name> as an IP Address",
367 : .argDescrip = NULL
368 : },
369 : {
370 : .longName = "case-sensitive",
371 : .shortName = 0,
372 : .argInfo = POPT_ARG_NONE,
373 : .arg = NULL,
374 : .val = OPT_CASE_SENSITIVE,
375 : .descrip = "Don't uppercase the name before sending",
376 : .argDescrip = NULL
377 : },
378 851 : POPT_COMMON_SAMBA
379 851 : POPT_COMMON_VERSION
380 : POPT_TABLEEND
381 : };
382 :
383 851 : mem_ctx = talloc_init("nmblookup.c/main");
384 851 : if (mem_ctx == NULL) {
385 0 : exit(ENOMEM);
386 : }
387 :
388 851 : ok = samba_cmdline_init(mem_ctx,
389 : SAMBA_CMDLINE_CONFIG_CLIENT,
390 : false /* require_smbconf */);
391 851 : if (!ok) {
392 0 : DBG_ERR("Failed to init cmdline parser!\n");
393 0 : TALLOC_FREE(mem_ctx);
394 0 : exit(1);
395 : }
396 :
397 851 : pc = samba_popt_get_context(getprogname(),
398 : argc,
399 : argv,
400 : long_options,
401 : POPT_CONTEXT_KEEP_FIRST);
402 851 : if (pc == NULL) {
403 0 : DBG_ERR("Failed to setup popt context!\n");
404 0 : TALLOC_FREE(mem_ctx);
405 0 : exit(1);
406 : }
407 :
408 851 : poptSetOtherOptionHelp(pc, "<NODE> ...");
409 :
410 1530 : while ((opt = poptGetNextOpt(pc)) != -1) {
411 393 : switch(opt) {
412 0 : case OPT_BROADCAST_ADDRESS:
413 0 : options.broadcast_address = poptGetOptArg(pc);
414 0 : break;
415 393 : case OPT_UNICAST_ADDRESS:
416 393 : options.unicast_address = poptGetOptArg(pc);
417 393 : break;
418 0 : case OPT_FIND_MASTER:
419 0 : options.find_master = true;
420 0 : break;
421 0 : case OPT_WINS_LOOKUP:
422 0 : options.wins_lookup = true;
423 0 : break;
424 0 : case OPT_NODE_STATUS:
425 0 : options.node_status = true;
426 0 : break;
427 0 : case OPT_ROOT_PORT:
428 0 : options.root_port = true;
429 0 : break;
430 0 : case OPT_LOOKUP_BY_IP:
431 0 : options.lookup_by_ip = true;
432 0 : break;
433 0 : case OPT_CASE_SENSITIVE:
434 0 : options.case_sensitive = true;
435 0 : break;
436 0 : case POPT_ERROR_BADOPT:
437 0 : fprintf(stderr, "\nInvalid option %s: %s\n\n",
438 : poptBadOption(pc, 0), poptStrerror(opt));
439 0 : poptPrintUsage(pc, stderr, 0);
440 0 : exit(1);
441 : }
442 : }
443 :
444 : /* swallow argv[0] */
445 851 : poptGetArg(pc);
446 :
447 851 : if(!poptPeekArg(pc)) {
448 0 : poptPrintUsage(pc, stderr, 0);
449 0 : TALLOC_FREE(mem_ctx);
450 0 : exit(1);
451 : }
452 :
453 851 : lp_ctx = samba_cmdline_get_lp_ctx();
454 :
455 851 : load_interface_list(mem_ctx, lp_ctx, &ifaces);
456 :
457 851 : ev = s4_event_context_init(mem_ctx);
458 :
459 1702 : while (poptPeekArg(pc)) {
460 851 : const char *name = poptGetArg(pc);
461 :
462 851 : ret &= process_one(lp_ctx,
463 : ev,
464 : ifaces,
465 : name,
466 : lpcfg_nbt_port(lp_ctx));
467 : }
468 :
469 851 : poptFreeContext(pc);
470 851 : TALLOC_FREE(mem_ctx);
471 :
472 851 : if (!ret) {
473 65 : return 1;
474 : }
475 :
476 762 : return 0;
477 : }
|