r26401: Don't cache interfaces context in libnetif.
[samba.git] / source4 / utils / nmblookup.c
index 6505e6df3cad1e05394970f61d6e2ee16aec5183..6c849fc0dbde1e6d0d24b05813271aec8e72b91e 100644 (file)
@@ -1,11 +1,14 @@
 /* 
    Unix SMB/CIFS implementation.
+
    NBT client - used to lookup netbios names
-   Copyright (C) Andrew Tridgell 1994-1998
+
+   Copyright (C) Andrew Tridgell 1994-2005
+   Copyright (C) Jelmer Vernooij 2003 (Conversion to popt)
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
 */
 
 #include "includes.h"
-
-extern BOOL AllowDebugChange;
-
-static BOOL give_flags = False;
-static BOOL use_bcast = True;
-static BOOL got_bcast = False;
-static struct in_addr bcast_addr;
-static BOOL recursion_desired = False;
-static BOOL translate_addresses = False;
-static int ServerFD= -1;
-static int RootPort = False;
-static BOOL find_status=False;
-
-/****************************************************************************
-  open the socket communication
-  **************************************************************************/
-static BOOL open_sockets(void)
+#include "lib/cmdline/popt_common.h"
+#include "lib/socket/socket.h"
+#include "system/network.h"
+#include "system/locale.h"
+#include "lib/socket/netif.h"
+#include "librpc/gen_ndr/nbt.h"
+#include "libcli/nbt/libnbt.h"
+#include "param/param.h"
+
+/* command line options */
+static struct {
+       const char *broadcast_address;
+       const char *unicast_address;
+       bool find_master;
+       bool wins_lookup;
+       bool node_status;
+       bool root_port;
+       bool lookup_by_ip;
+       bool case_sensitive;
+} options;
+
+/*
+  clean any binary from a node name
+*/
+static const char *clean_name(TALLOC_CTX *mem_ctx, const char *name)
 {
-  ServerFD = open_socket_in( SOCK_DGRAM,
-                             (RootPort ? 137 : 0),
-                             (RootPort ?   0 : 3),
-                             interpret_addr(lp_socket_address()), True );
-
-  if (ServerFD == -1)
-    return(False);
+       char *ret = talloc_strdup(mem_ctx, name);
+       int i;
+       for (i=0;ret[i];i++) {
+               if (!isprint((unsigned char)ret[i])) ret[i] = '.';
+       }
+       return ret;
+}
 
-  set_socket_options( ServerFD, "SO_BROADCAST" );
+/*
+  turn a node status flags field into a string
+*/
+static char *node_status_flags(TALLOC_CTX *mem_ctx, uint16_t flags)
+{
+       char *ret;
+       const char *group = "       ";
+       const char *type = "B";
 
-  DEBUG(3, ("Socket opened.\n"));
-  return True;
-}
+       if (flags & NBT_NM_GROUP) {
+               group = "<GROUP>";
+       }
 
+       switch (flags & NBT_NM_OWNER_TYPE) {
+       case NBT_NODE_B: 
+               type = "B";
+               break;
+       case NBT_NODE_P: 
+               type = "P";
+               break;
+       case NBT_NODE_M: 
+               type = "M";
+               break;
+       case NBT_NODE_H: 
+               type = "H";
+               break;
+       }
 
-/****************************************************************************
-usage on the program
-****************************************************************************/
-static void usage(void)
-{
-  d_printf("Usage: nmblookup [options] name\n");
-  d_printf("Version %s\n",VERSION);
-  d_printf("\t-d debuglevel         set the debuglevel\n");
-  d_printf("\t-B broadcast address  the address to use for broadcasts\n");
-  d_printf("\t-f                    list the NMB flags returned\n");
-  d_printf("\t-U unicast   address  the address to use for unicast\n");
-  d_printf("\t-M                    searches for a master browser\n");
-  d_printf("\t-R                    set recursion desired in packet\n");
-  d_printf("\t-S                    lookup node status as well\n");
-  d_printf("\t-T                    translate IP addresses into names\n");
-  d_printf("\t-r                    Use root port 137 (Win95 only replies to this)\n");
-  d_printf("\t-A                    Do a node status on <name> as an IP Address\n");
-  d_printf("\t-i NetBIOS scope      Use the given NetBIOS scope for name queries\n");
-  d_printf("\t-s smb.conf file      Use the given path to the smb.conf file\n");
-  d_printf("\t-h                    Print this help message.\n");
-  d_printf("\n  If you specify -M and name is \"-\", nmblookup looks up __MSBROWSE__<01>\n");
-  d_printf("\n");
-}
+       ret = talloc_asprintf(mem_ctx, "%s %s", group, type);
 
-/****************************************************************************
-turn a node status flags field into a string
-****************************************************************************/
-static char *node_status_flags(uint8_t flags)
-{
-       static fstring ret;
-       fstrcpy(ret,"");
-       
-       fstrcat(ret, (flags & 0x80) ? "<GROUP> " : "        ");
-       if ((flags & 0x60) == 0x00) fstrcat(ret,"B ");
-       if ((flags & 0x60) == 0x20) fstrcat(ret,"P ");
-       if ((flags & 0x60) == 0x40) fstrcat(ret,"M ");
-       if ((flags & 0x60) == 0x60) fstrcat(ret,"H ");
-       if (flags & 0x10) fstrcat(ret,"<DEREGISTERING> ");
-       if (flags & 0x08) fstrcat(ret,"<CONFLICT> ");
-       if (flags & 0x04) fstrcat(ret,"<ACTIVE> ");
-       if (flags & 0x02) fstrcat(ret,"<PERMANENT> ");
+       if (flags & NBT_NM_DEREGISTER) {
+               ret = talloc_asprintf_append_buffer(ret, " <DEREGISTERING>");
+       }
+       if (flags & NBT_NM_CONFLICT) {
+               ret = talloc_asprintf_append_buffer(ret, " <CONFLICT>");
+       }
+       if (flags & NBT_NM_ACTIVE) {
+               ret = talloc_asprintf_append_buffer(ret, " <ACTIVE>");
+       }
+       if (flags & NBT_NM_PERMANENT) {
+               ret = talloc_asprintf_append_buffer(ret, " <PERMANENT>");
+       }
        
        return ret;
 }
 
-/****************************************************************************
-turn the NMB Query flags into a string
-****************************************************************************/
-static char *query_flags(int flags)
+/* do a single node status */
+static bool do_node_status(struct nbt_name_socket *nbtsock,
+                          const char *addr, uint16_t port)
 {
-       static fstring ret1;
-       fstrcpy(ret1, "");
-
-       if (flags & NM_FLAGS_RS) fstrcat(ret1, "Response ");
-       if (flags & NM_FLAGS_AA) fstrcat(ret1, "Authoritative ");
-       if (flags & NM_FLAGS_TC) fstrcat(ret1, "Truncated ");
-       if (flags & NM_FLAGS_RD) fstrcat(ret1, "Recursion_Desired ");
-       if (flags & NM_FLAGS_RA) fstrcat(ret1, "Recursion_Available ");
-       if (flags & NM_FLAGS_B)  fstrcat(ret1, "Broadcast ");
+       struct nbt_name_status io;
+       NTSTATUS status;
+
+       io.in.name.name = "*";
+       io.in.name.type = NBT_NAME_CLIENT;
+       io.in.name.scope = NULL;
+       io.in.dest_addr = addr;
+       io.in.dest_port = port;
+       io.in.timeout = 1;
+       io.in.retries = 2;
+
+       status = nbt_name_status(nbtsock, nbtsock, &io);
+       if (NT_STATUS_IS_OK(status)) {
+               int i;
+               printf("Node status reply from %s\n",
+                      io.out.reply_from);
+               for (i=0;i<io.out.status.num_names;i++) {
+                       d_printf("\t%-16s <%02x>  %s\n", 
+                                clean_name(nbtsock, io.out.status.names[i].name),
+                                io.out.status.names[i].type,
+                                node_status_flags(nbtsock, io.out.status.names[i].nb_flags));
+               }
+               printf("\n\tMAC Address = %02X-%02X-%02X-%02X-%02X-%02X\n",
+                      io.out.status.statistics.unit_id[0],
+                      io.out.status.statistics.unit_id[1],
+                      io.out.status.statistics.unit_id[2],
+                      io.out.status.statistics.unit_id[3],
+                      io.out.status.statistics.unit_id[4],
+                      io.out.status.statistics.unit_id[5]);
+               return true;
+       }
 
-       return ret1;
+       return false;
 }
 
-/****************************************************************************
-do a node status query
-****************************************************************************/
-static void do_node_status(int fd, const char *name, int type, struct in_addr ip)
+/* do a single node query */
+static NTSTATUS do_node_query(struct nbt_name_socket *nbtsock,
+                             const char *addr, 
+                             uint16_t port,
+                             const char *node_name, 
+                             enum nbt_name_type node_type,
+                             bool broadcast)
 {
-       struct nmb_name nname;
-       int count, i, j;
-       struct node_status *status;
-       fstring cleanname;
-
-       d_printf("Looking up status of %s\n",inet_ntoa(ip));
-       make_nmb_name(&nname, name, type);
-       status = node_status_query(fd,&nname,ip, &count);
-       if (status) {
-               for (i=0;i<count;i++) {
-                       fstrcpy(cleanname, status[i].name);
-                       for (j=0;cleanname[j];j++) {
-                               if (!isprint((int)cleanname[j])) cleanname[j] = '.';
-                       }
-                       d_printf("\t%-15s <%02x> - %s\n",
-                              cleanname,status[i].type,
-                              node_status_flags(status[i].flags));
-               }
-               SAFE_FREE(status);
+       struct nbt_name_query io;
+       NTSTATUS status;
+       int i;
+
+       io.in.name.name = node_name;
+       io.in.name.type = node_type;
+       io.in.name.scope = NULL;
+       io.in.dest_addr = addr;
+       io.in.dest_port = port;
+       io.in.broadcast = broadcast;
+       io.in.wins_lookup = options.wins_lookup;
+       io.in.timeout = 1;
+       io.in.retries = 2;
+
+       status = nbt_name_query(nbtsock, nbtsock, &io);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       for (i=0;i<io.out.num_addrs;i++) {
+               printf("%s %s<%02x>\n",
+                      io.out.reply_addrs[i],
+                      io.out.name.name,
+                      io.out.name.type);
        }
-       d_printf("\n");
+       if (options.node_status && io.out.num_addrs > 0) {
+               do_node_status(nbtsock, io.out.reply_addrs[0], port);
+       }
+
+       return status;
 }
 
 
-/****************************************************************************
-send out one query
-****************************************************************************/
-static BOOL query_one(const char *lookup, uint_t lookup_type)
+static bool process_one(struct loadparm_context *lp_ctx, 
+                       struct interface *ifaces, const char *name, int nbt_port)
 {
-       int j, count, flags = 0;
-       struct in_addr *ip_list=NULL;
-
-       if (got_bcast) {
-               d_printf("querying %s on %s\n", lookup, inet_ntoa(bcast_addr));
-               ip_list = name_query(ServerFD,lookup,lookup_type,use_bcast,
-                                    use_bcast?True:recursion_desired,
-                                    bcast_addr,&count, &flags, NULL);
-       } else {
-               struct in_addr *bcast;
-               for (j=iface_count() - 1;
-                    !ip_list && j >= 0;
-                    j--) {
-                       bcast = iface_n_bcast(j);
-                       d_printf("querying %s on %s\n", 
-                              lookup, inet_ntoa(*bcast));
-                       ip_list = name_query(ServerFD,lookup,lookup_type,
-                                            use_bcast,
-                                            use_bcast?True:recursion_desired,
-                                            *bcast,&count, &flags, NULL);
+       TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+       enum nbt_name_type node_type = NBT_NAME_CLIENT;
+       char *node_name, *p;
+       struct socket_address *all_zero_addr;
+       struct nbt_name_socket *nbtsock;
+       NTSTATUS status = NT_STATUS_OK;
+       bool ret = true;
+
+       if (!options.case_sensitive) {
+               name = strupper_talloc(tmp_ctx, name);
+       }
+       
+       if (options.find_master) {
+               node_type = NBT_NAME_MASTER;
+               if (*name == '-' || *name == '_') {
+                       name = "\01\02__MSBROWSE__\02";
+                       node_type = NBT_NAME_MS;
                }
        }
 
-       if (!ip_list) return False;
+       p = strchr(name, '#');
+       if (p) {
+               node_name = talloc_strndup(tmp_ctx, name, PTR_DIFF(p,name));
+               node_type = (enum nbt_name_type)strtol(p+1, NULL, 16);
+       } else {
+               node_name = talloc_strdup(tmp_ctx, name);
+       }
 
-       if (give_flags)
-         d_printf("Flags: %s\n", query_flags(flags));
+       nbtsock = nbt_name_socket_init(tmp_ctx, NULL);
+       
+       if (options.root_port) {
+               all_zero_addr = socket_address_from_strings(tmp_ctx, nbtsock->sock->backend_name, 
+                                                           "0.0.0.0", NBT_NAME_SERVICE_PORT);
+               
+               if (!all_zero_addr) {
+                       talloc_free(tmp_ctx);
+                       return false;
+               }
 
-       for (j=0;j<count;j++) {
-               if (translate_addresses) {
-                       struct hostent *host = gethostbyaddr((char *)&ip_list[j], sizeof(ip_list[j]), AF_INET);
-                       if (host) {
-                               d_printf("%s, ", host -> h_name);
-                       }
+               status = socket_listen(nbtsock->sock, all_zero_addr, 0, 0);
+               if (!NT_STATUS_IS_OK(status)) {
+                       printf("Failed to bind to local port 137 - %s\n", nt_errstr(status));
+                       talloc_free(tmp_ctx);
+                       return false;
                }
-               d_printf("%s %s<%02x>\n",inet_ntoa(ip_list[j]),lookup, lookup_type);
        }
 
-       /* We can only do find_status if the ip address returned
-          was valid - ie. name_query returned true.
-       */
-       if (find_status) {
-               do_node_status(ServerFD, lookup, lookup_type, ip_list[0]);
+       if (options.lookup_by_ip) {
+               ret = do_node_status(nbtsock, name, nbt_port);
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       if (options.broadcast_address) {
+               status = do_node_query(nbtsock, options.broadcast_address, nbt_port,
+                                      node_name, node_type, true);
+       } else if (options.unicast_address) {
+               status = do_node_query(nbtsock, options.unicast_address, 
+                                      nbt_port, node_name, node_type, false);
+       } else {
+               int i, num_interfaces;
+
+               num_interfaces = iface_count(ifaces);
+               for (i=0;i<num_interfaces;i++) {
+                       const char *bcast = iface_n_bcast(ifaces, i);
+                       if (bcast == NULL) continue;
+                       status = do_node_query(nbtsock, bcast, nbt_port, 
+                                              node_name, node_type, true);
+                       if (NT_STATUS_IS_OK(status)) break;
+               }
        }
 
-       safe_free(ip_list);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("Lookup failed - %s\n", nt_errstr(status));
+               ret = false;
+       }
 
-       return (ip_list != NULL);
+       talloc_free(tmp_ctx);
+       return ret;
 }
 
-
-/****************************************************************************
+/*
   main program
-****************************************************************************/
-int main(int argc,char *argv[])
+*/
+int main(int argc, const char *argv[])
 {
-  int opt;
-  uint_t lookup_type = 0x0;
-  fstring lookup;
-  extern int optind;
-  extern char *optarg;
-  BOOL find_master=False;
-  int i;
-  BOOL lookup_by_ip = False;
-  int commandline_debuglevel = -2;
-
-  DEBUGLEVEL = 1;
-  /* Prevent smb.conf setting from overridding */
-  AllowDebugChange = False;
-
-  *lookup = 0;
-
-  setup_logging(argv[0], DEBUG_STDOUT);
-
-  while ((opt = getopt(argc, argv, "d:fB:U:i:s:SMrhART")) != EOF)
-    switch (opt)
-      {
-      case 'B':
-       bcast_addr = *interpret_addr2(optarg);
-       got_bcast = True;
-       use_bcast = True;
-       break;
-      case 'f':
-       give_flags = True;
-       break;
-      case 'U':
-       bcast_addr = *interpret_addr2(optarg);
-       got_bcast = True;
-       use_bcast = False;
-       break;
-      case 'T':
-        translate_addresses = !translate_addresses;
-       break;
-      case 'i':
-             lp_set_cmdline("netbios scope", optarg);
-       break;
-      case 'M':
-       find_master = True;
-       break;
-      case 'S':
-       find_status = True;
-       break;
-      case 'R':
-       recursion_desired = True;
-       break;
-      case 'd':
-       commandline_debuglevel = DEBUGLEVEL = atoi(optarg);
-       break;
-      case 's':
-       pstrcpy(dyn_CONFIGFILE, optarg);
-       break;
-      case 'r':
-        RootPort = True;
-        break;
-      case 'h':
-       usage();
-       exit(0);
-       break;
-      case 'A':
-        lookup_by_ip = True;
-        break;
-      default:
-       usage();
-       exit(1);
-      }
-
-  if (argc < 2) {
-    usage();
-    exit(1);
-  }
-
-  if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
-    fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
-  }
-
-  /*
-   * Ensure we reset DEBUGLEVEL if someone specified it
-   * on the command line.
-   */
-
-  if(commandline_debuglevel != -2)
-    DEBUGLEVEL = commandline_debuglevel;
-
-  load_interfaces();
-  if (!open_sockets()) return(1);
-
-  for (i=optind;i<argc;i++)
-  {
-      char *p;
-      struct in_addr ip;
-
-      fstrcpy(lookup,argv[i]);
-
-      if(lookup_by_ip)
-      {
-        fstrcpy(lookup,"*");
-        ip = *interpret_addr2(argv[i]);
-       do_node_status(ServerFD, lookup, lookup_type, ip);
-        continue;
-      }
-
-      if (find_master) {
-       if (*lookup == '-') {
-         fstrcpy(lookup,"\01\02__MSBROWSE__\02");
-         lookup_type = 1;
-       } else {
-         lookup_type = 0x1d;
+       bool ret = true;
+       struct interface *ifaces;
+       poptContext pc;
+       int opt;
+       enum {
+               OPT_BROADCAST_ADDRESS   = 1000,
+               OPT_UNICAST_ADDRESS,
+               OPT_FIND_MASTER,
+               OPT_WINS_LOOKUP,
+               OPT_NODE_STATUS,
+               OPT_ROOT_PORT,
+               OPT_LOOKUP_BY_IP,
+               OPT_CASE_SENSITIVE
+       };
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+               { "broadcast", 'B', POPT_ARG_STRING, NULL, OPT_BROADCAST_ADDRESS,
+                 "Specify address to use for broadcasts", "BROADCAST-ADDRESS" },
+
+               { "unicast", 'U', POPT_ARG_STRING, NULL, OPT_UNICAST_ADDRESS,
+                 "Specify address to use for unicast", NULL },
+
+               { "master-browser", 'M', POPT_ARG_NONE, NULL, OPT_FIND_MASTER,
+                 "Search for a master browser", NULL },
+
+               { "wins", 'W', POPT_ARG_NONE, NULL, OPT_WINS_LOOKUP,
+                 "Do a WINS lookup", NULL },
+
+               { "status", 'S', POPT_ARG_NONE, NULL, OPT_NODE_STATUS, 
+                 "Lookup node status as well", NULL },
+
+               { "root-port", 'r', POPT_ARG_NONE, NULL, OPT_ROOT_PORT, 
+                 "Use root port 137 (Win95 only replies to this)", NULL },
+
+               { "lookup-by-ip", 'A', POPT_ARG_NONE, NULL, OPT_LOOKUP_BY_IP, 
+                 "Do a node status on <name> as an IP Address", NULL },
+
+               { "case-sensitive", 0, POPT_ARG_NONE, NULL, OPT_CASE_SENSITIVE, 
+                 "Don't uppercase the name before sending", NULL },
+
+               POPT_COMMON_SAMBA
+               { 0, 0, 0, 0 }
+       };
+       
+       pc = poptGetContext("nmblookup", argc, argv, long_options, 
+                           POPT_CONTEXT_KEEP_FIRST);
+
+       poptSetOtherOptionHelp(pc, "<NODE> ...");
+
+       while ((opt = poptGetNextOpt(pc)) != -1) {
+               switch(opt) {
+               case OPT_BROADCAST_ADDRESS:
+                       options.broadcast_address = poptGetOptArg(pc);
+                       break;
+               case OPT_UNICAST_ADDRESS:
+                       options.unicast_address = poptGetOptArg(pc);
+                       break;
+               case OPT_FIND_MASTER:
+                       options.find_master = true;
+                       break;
+               case OPT_WINS_LOOKUP:
+                       options.wins_lookup = true;
+                       break;
+               case OPT_NODE_STATUS:
+                       options.node_status = true;
+                       break;
+               case OPT_ROOT_PORT:
+                       options.root_port = true;
+                       break;
+               case OPT_LOOKUP_BY_IP:
+                       options.lookup_by_ip = true;
+                       break;
+               case OPT_CASE_SENSITIVE:
+                       options.case_sensitive = true;
+                       break;
+               }
+       }
+
+       /* swallow argv[0] */
+       poptGetArg(pc);
+
+       if(!poptPeekArg(pc)) { 
+               poptPrintUsage(pc, stderr, 0);
+               exit(1);
+       }
+
+       load_interfaces(lp_interfaces(cmdline_lp_ctx), &ifaces);
+       
+       while (poptPeekArg(pc)) {
+               const char *name = poptGetArg(pc);
+
+               ret &= process_one(cmdline_lp_ctx, ifaces, name, lp_nbt_port(cmdline_lp_ctx));
        }
-      }
-
-      p = strchr_m(lookup,'#');
-      if (p) {
-        *p = '\0';
-        sscanf(++p,"%x",&lookup_type);
-      }
-
-      if (!query_one(lookup, lookup_type)) {
-       d_printf( "name_query failed to find name %s", lookup );
-        if( 0 != lookup_type )
-          d_printf( "#%02x", lookup_type );
-        d_printf( "\n" );
-      }
-  }
-  
-  return(0);
+
+       poptFreeContext(pc);
+
+       if (!ret) {
+               return 1;
+       }
+
+       return 0;
 }