A program to display a tree of domains, servers and shares similar to the
[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%s\n", sv->name);
297
298                         /* List shares */
299
300                         free_name_list(shares);
301                         shares = NULL;
302
303                         if (level == LEV_SERVER ||
304                             !get_shares(sv->name, user_info))
305                                 continue;
306
307                         for (sh = shares; sh; sh = sh->next) {
308                                 printf("\t\t%s\n", sh->name);
309                         }
310                 }
311         }
312
313         return True;
314 }
315
316 /****************************************************************************
317   main program
318 ****************************************************************************/
319  int main(int argc,char *argv[])
320 {
321         extern char *optarg;
322         extern int optind;
323         extern FILE *dbf;
324         int opt;
325         char *p;
326         pstring servicesf = CONFIGFILE;
327         struct user_auth_info user_info;
328         BOOL got_pass = False;
329
330         /* Initialise samba stuff */
331
332         setlinebuf(stdout);
333
334         dbf = stderr;
335
336         setup_logging(argv[0],True);
337
338         TimeInit();
339
340         lp_load(servicesf,True,False,False);
341         load_interfaces();
342
343         if (getenv("USER")) {
344                 pstrcpy(user_info.username, getenv("USER"));
345
346                 if ((p=strchr(user_info.username, '%'))) {
347                         *p = 0;
348                         pstrcpy(user_info.password, p+1);
349                         got_pass = True;
350                         memset(strchr(getenv("USER"), '%') + 1, 'X',
351                                strlen(user_info.password));
352                 }
353         }
354
355         pstrcpy(user_info.workgroup, lp_workgroup());
356
357         /* Parse command line args */
358
359         while ((opt = getopt(argc, argv, "U:hd:W:DS")) != EOF) {
360                 switch (opt) {
361                 case 'U':
362                         pstrcpy(user_info.username,optarg);
363                         p = strchr(user_info.username,'%');
364                         if (p) {
365                                 *p = 0;
366                                 pstrcpy(user_info.password, p+1);
367                                 got_pass = 1;
368                         }
369                         break;
370
371                 case 'h':
372                         usage();
373                         exit(1);
374
375                 case 'd':
376                         DEBUGLEVEL = atoi(optarg);
377                         break;
378
379                 case 'W':
380                         pstrcpy(user_info.workgroup, optarg);
381                         break;
382
383                 case 'D':
384                         level = LEV_WORKGROUP;
385                         break;
386
387                 case 'S':
388                         level = LEV_SERVER;
389                         break;
390
391                 default:
392                         printf("Unknown option %c (%d)\n", (char)opt, opt);
393                         exit(1);
394                 }
395         }
396
397         argc -= optind;
398         argv += optind;
399         
400         if (argc > 0) {
401                 usage();
402                 exit(1);
403         }
404
405         if (!got_pass) {
406                 char *pass = getpass("Password: ");
407                 if (pass) {
408                         pstrcpy(user_info.password, pass);
409                 }
410                 got_pass = True;
411         }
412
413         /* Now do our stuff */
414
415         if (!print_tree(&user_info))
416                 return 1;
417
418         return 0;
419 }