nicer smbtree output
[samba.git] / source / utils / smbtree.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Network neighbourhood browser.
4    Version 3.0
5    
6    Copyright (C) Tim Potter      2000
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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 struct user_auth_info {
26         pstring username;
27         pstring password;
28         pstring workgroup;
29 };
30
31 /* How low can we go? */
32
33 enum tree_level {LEV_WORKGROUP, LEV_SERVER, LEV_SHARE};
34 enum tree_level level = LEV_SHARE;
35
36 static void usage(void)
37 {
38         printf(
39 "Usage: smbtree [options]\n\
40 \n\
41 \t-d debuglevel           set debug output level\n\
42 \t-U username             user to autheticate as\n\
43 \t-W workgroup            workgroup of user to authenticate as\n\
44 \t-D                      list only domains (workgroups) of tree\n\
45 \t-S                      list domains and servers of tree\n\
46 \n\
47 The username can be of the form username%%password or\n\
48 workgroup\\username%%password.\n\n\
49 ");
50 }
51
52 /* Holds a list of workgroups or servers */
53
54 struct name_list {
55         struct name_list *prev, *next;
56         pstring name, comment;
57         uint32 server_type;
58 };
59
60 static struct name_list *workgroups, *servers, *shares;
61
62 static void free_name_list(struct name_list *list)
63 {
64         while(list)
65                 DLIST_REMOVE(list, list);
66 }
67
68 static void add_name(const char *machine_name, uint32 server_type,
69                      const char *comment, void *state)
70 {
71         struct name_list **name_list = (struct name_list **)state;
72         struct name_list *new_name;
73
74         new_name = (struct name_list *)malloc(sizeof(struct name_list));
75
76         if (!new_name)
77                 return;
78
79         ZERO_STRUCTP(new_name);
80
81         pstrcpy(new_name->name, machine_name);
82         pstrcpy(new_name->comment, comment);
83         new_name->server_type = server_type;
84
85         DLIST_ADD(*name_list, new_name);
86 }
87
88 /* Return a cli_state pointing at the IPC$ share for the given workgroup */
89
90 static struct cli_state *get_ipc_connect(char *server,
91                                          struct user_auth_info *user_info)
92 {
93         struct nmb_name calling, called;
94         extern struct in_addr ipzero;
95         struct in_addr server_ip = ipzero;
96         struct cli_state *cli;
97         pstring myname;
98
99         get_myname(myname);
100
101         make_nmb_name(&called, myname, 0x0);
102         make_nmb_name(&calling, server, 0x20);
103
104         if (is_ipaddress(server))
105                 if (!resolve_name(server, &server_ip, 0x20))
106                         return False;
107                 
108  again:
109         if (!(cli = cli_initialise(NULL))) {
110                 DEBUG(4, ("Unable to initialise cli structure\n"));
111                 goto error;
112         }
113
114         if (!cli_connect(cli, server, &server_ip)) {
115                 DEBUG(4, ("Unable to connect to %s\n", server));
116                 goto error;
117         }
118
119         if (!cli_session_request(cli, &calling, &called)) {
120                 cli_shutdown(cli);
121                 if (!strequal(called.name, "*SMBSERVER")) {
122                         make_nmb_name(&called , "*SMBSERVER", 0x20);
123                         goto again;
124                 }
125                 DEBUG(4, ("Session request failed to %s\n", called.name));
126                 goto error;
127         }
128
129         if (!cli_negprot(cli)) {
130                 DEBUG(4, ("Negprot failed\n"));
131                 goto error;
132         }
133
134         if (!cli_session_setup(cli, user_info->username, user_info->password, 
135                                strlen(user_info->password),
136                                user_info->password, 
137                                strlen(user_info->password), server) &&
138             /* try an anonymous login if it failed */
139             !cli_session_setup(cli, "", "", 1,"", 0, server)) {
140                 DEBUG(4, ("Session setup failed\n"));
141                 goto error;
142         }
143
144         DEBUG(4,(" session setup ok\n"));
145
146         if (!cli_send_tconX(cli, "IPC$", "?????",
147                             user_info->password, 
148                             strlen(user_info->password)+1)) {
149                 DEBUG(4, ("Tconx failed\n"));
150                 goto error;
151         }
152
153         return cli;
154
155         /* Clean up after error */
156
157  error:
158         if (cli && cli->initialised)
159                 cli_shutdown(cli);
160
161         free(cli);
162         return NULL;
163 }
164
165 /* Return the IP address and workgroup of a master browser on the 
166    network. */
167
168 static BOOL find_master_ip_bcast(pstring workgroup, struct in_addr *server_ip)
169 {
170         struct in_addr *ip_list;
171         int i, count;
172
173         /* Go looking for workgroups by broadcasting on the local network */ 
174
175         if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
176                 return False;
177         }
178
179         for (i = 0; i < count; i++) {
180                 static fstring name;
181
182                 if (!name_status_find(0x1d, ip_list[i], name))
183                         return False;
184
185                 if (!find_master_ip(name, server_ip))
186                         return False;
187
188                 pstrcpy(workgroup, name);
189
190                 DEBUG(4, ("found master browser %s, %s\n", 
191                           name, inet_ntoa(ip_list[i])));
192
193                 return True;
194         }
195
196         return False;
197 }
198
199 /****************************************************************************
200   display tree of smb workgroups, servers and shares
201 ****************************************************************************/
202 static BOOL get_workgroups(struct user_auth_info *user_info)
203 {
204         struct cli_state *cli;
205         struct in_addr server_ip;
206         pstring master_workgroup;
207
208         /* Try to connect to a #1d name of our current workgroup.  If that
209            doesn't work broadcast for a master browser and then jump off
210            that workgroup. */
211
212         pstrcpy(master_workgroup, lp_workgroup());
213
214         if (!find_master_ip(lp_workgroup(), &server_ip)) {
215                 DEBUG(4, ("Unable to find master browser for workgroup %s\n", 
216                           master_workgroup));
217                 if (!find_master_ip_bcast(master_workgroup, &server_ip)) {
218                         DEBUG(4, ("Unable to find master browser by "
219                                   "broadcast\n"));
220                         return False;
221                 }
222         }
223
224         if (!(cli = get_ipc_connect(inet_ntoa(server_ip), user_info)))
225                 return False;
226
227         if (!cli_NetServerEnum(cli, master_workgroup, 
228                                SV_TYPE_DOMAIN_ENUM, add_name, &workgroups))
229                 return False;
230
231         return True;
232 }
233
234 /* Retrieve the list of servers for a given workgroup */
235
236 static BOOL get_servers(char *workgroup, struct user_auth_info *user_info)
237 {
238         struct cli_state *cli;
239         struct in_addr server_ip;
240
241         /* Open an IPC$ connection to the master browser for the workgroup */
242
243         if (!find_master_ip(workgroup, &server_ip)) {
244                 DEBUG(4, ("Cannot find master browser for workgroup %s\n",
245                           workgroup));
246                 return False;
247         }
248
249         if (!(cli = get_ipc_connect(inet_ntoa(server_ip), user_info)))
250                 return False;
251
252         if (!cli_NetServerEnum(cli, workgroup, SV_TYPE_ALL, add_name, 
253                                &servers))
254                 return False;
255
256         return True;
257 }
258
259 static BOOL get_shares(char *server_name, struct user_auth_info *user_info)
260 {
261         struct cli_state *cli;
262
263         if (!(cli = get_ipc_connect(server_name, user_info)))
264                 return False;
265
266         if (!cli_RNetShareEnum(cli, add_name, &shares))
267                 return False;
268
269         return True;
270 }
271
272 static BOOL print_tree(struct user_auth_info *user_info)
273 {
274         struct name_list *wg, *sv, *sh;
275
276         /* List workgroups */
277
278         if (!get_workgroups(user_info))
279                 return False;
280
281         for (wg = workgroups; wg; wg = wg->next) {
282
283                 printf("%s\n", wg->name);
284
285                 /* List servers */
286
287                 free_name_list(servers);
288                 servers = NULL;
289
290                 if (level == LEV_WORKGROUP || 
291                     !get_servers(wg->name, user_info))
292                         continue;
293
294                 for (sv = servers; sv; sv = sv->next) {
295
296                         printf("\t\\\\%-15s\t\t%s\n", 
297                                sv->name, sv->comment);
298
299                         /* List shares */
300
301                         free_name_list(shares);
302                         shares = NULL;
303
304                         if (level == LEV_SERVER ||
305                             !get_shares(sv->name, user_info))
306                                 continue;
307
308                         for (sh = shares; sh; sh = sh->next) {
309                                 printf("\t\t\\\\%s\\%-15s\t%s\n", 
310                                        sv->name, sh->name, sh->comment);
311                         }
312                 }
313         }
314
315         return True;
316 }
317
318 /****************************************************************************
319   main program
320 ****************************************************************************/
321  int main(int argc,char *argv[])
322 {
323         extern char *optarg;
324         extern int optind;
325         extern FILE *dbf;
326         int opt;
327         char *p;
328         pstring servicesf = CONFIGFILE;
329         struct user_auth_info user_info;
330         BOOL got_pass = False;
331
332         /* Initialise samba stuff */
333
334         setlinebuf(stdout);
335
336         dbf = stderr;
337
338         setup_logging(argv[0],True);
339
340         TimeInit();
341
342         lp_load(servicesf,True,False,False);
343         load_interfaces();
344
345         if (getenv("USER")) {
346                 pstrcpy(user_info.username, getenv("USER"));
347
348                 if ((p=strchr(user_info.username, '%'))) {
349                         *p = 0;
350                         pstrcpy(user_info.password, p+1);
351                         got_pass = True;
352                         memset(strchr(getenv("USER"), '%') + 1, 'X',
353                                strlen(user_info.password));
354                 }
355         }
356
357         pstrcpy(user_info.workgroup, lp_workgroup());
358
359         /* Parse command line args */
360
361         while ((opt = getopt(argc, argv, "U:hd:W:DS")) != EOF) {
362                 switch (opt) {
363                 case 'U':
364                         pstrcpy(user_info.username,optarg);
365                         p = strchr(user_info.username,'%');
366                         if (p) {
367                                 *p = 0;
368                                 pstrcpy(user_info.password, p+1);
369                                 got_pass = 1;
370                         }
371                         break;
372
373                 case 'h':
374                         usage();
375                         exit(1);
376
377                 case 'd':
378                         DEBUGLEVEL = atoi(optarg);
379                         break;
380
381                 case 'W':
382                         pstrcpy(user_info.workgroup, optarg);
383                         break;
384
385                 case 'D':
386                         level = LEV_WORKGROUP;
387                         break;
388
389                 case 'S':
390                         level = LEV_SERVER;
391                         break;
392
393                 default:
394                         printf("Unknown option %c (%d)\n", (char)opt, opt);
395                         exit(1);
396                 }
397         }
398
399         argc -= optind;
400         argv += optind;
401         
402         if (argc > 0) {
403                 usage();
404                 exit(1);
405         }
406
407         if (!got_pass) {
408                 char *pass = getpass("Password: ");
409                 if (pass) {
410                         pstrcpy(user_info.password, pass);
411                 }
412                 got_pass = True;
413         }
414
415         /* Now do our stuff */
416
417         if (!print_tree(&user_info))
418                 return 1;
419
420         return 0;
421 }