1/* 2 Unix SMB/CIFS implementation. 3 NBT client - used to lookup netbios names 4 Copyright (C) Andrew Tridgell 1994-1998 5 Copyright (C) Jelmer Vernooij 2003 (Conversion to popt) 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. 19 20*/ 21 22#include "includes.h" 23 24extern bool AllowDebugChange; 25 26static bool give_flags = false; 27static bool use_bcast = true; 28static bool got_bcast = false; 29static struct sockaddr_storage bcast_addr; 30static bool recursion_desired = false; 31static bool translate_addresses = false; 32static int ServerFD= -1; 33static bool RootPort = false; 34static bool find_status = false; 35 36/**************************************************************************** 37 Open the socket communication. 38**************************************************************************/ 39 40static bool open_sockets(void) 41{ 42 struct sockaddr_storage ss; 43 const char *sock_addr = lp_socket_address(); 44 45 if (!interpret_string_addr(&ss, sock_addr, 46 AI_NUMERICHOST|AI_PASSIVE)) { 47 DEBUG(0,("open_sockets: unable to get socket address " 48 "from string %s", sock_addr)); 49 return false; 50 } 51 ServerFD = open_socket_in( SOCK_DGRAM, 52 (RootPort ? 137 : 0), 53 (RootPort ? 0 : 3), 54 &ss, true ); 55 56 if (ServerFD == -1) { 57 return false; 58 } 59 60 set_socket_options( ServerFD, "SO_BROADCAST" ); 61 62 DEBUG(3, ("Socket opened.\n")); 63 return true; 64} 65 66/**************************************************************************** 67turn a node status flags field into a string 68****************************************************************************/ 69static char *node_status_flags(unsigned char flags) 70{ 71 static fstring ret; 72 fstrcpy(ret,""); 73 74 fstrcat(ret, (flags & 0x80) ? "<GROUP> " : " "); 75 if ((flags & 0x60) == 0x00) fstrcat(ret,"B "); 76 if ((flags & 0x60) == 0x20) fstrcat(ret,"P "); 77 if ((flags & 0x60) == 0x40) fstrcat(ret,"M "); 78 if ((flags & 0x60) == 0x60) fstrcat(ret,"H "); 79 if (flags & 0x10) fstrcat(ret,"<DEREGISTERING> "); 80 if (flags & 0x08) fstrcat(ret,"<CONFLICT> "); 81 if (flags & 0x04) fstrcat(ret,"<ACTIVE> "); 82 if (flags & 0x02) fstrcat(ret,"<PERMANENT> "); 83 84 return ret; 85} 86 87/**************************************************************************** 88 Turn the NMB Query flags into a string. 89****************************************************************************/ 90 91static char *query_flags(int flags) 92{ 93 static fstring ret1; 94 fstrcpy(ret1, ""); 95 96 if (flags & NM_FLAGS_RS) fstrcat(ret1, "Response "); 97 if (flags & NM_FLAGS_AA) fstrcat(ret1, "Authoritative "); 98 if (flags & NM_FLAGS_TC) fstrcat(ret1, "Truncated "); 99 if (flags & NM_FLAGS_RD) fstrcat(ret1, "Recursion_Desired "); 100 if (flags & NM_FLAGS_RA) fstrcat(ret1, "Recursion_Available "); 101 if (flags & NM_FLAGS_B) fstrcat(ret1, "Broadcast "); 102 103 return ret1; 104} 105 106/**************************************************************************** 107 Do a node status query. 108****************************************************************************/ 109 110static void do_node_status(int fd, 111 const char *name, 112 int type, 113 struct sockaddr_storage *pss) 114{ 115 struct nmb_name nname; 116 int count, i, j; 117 NODE_STATUS_STRUCT *status; 118 struct node_status_extra extra; 119 fstring cleanname; 120 char addr[INET6_ADDRSTRLEN]; 121 122 print_sockaddr(addr, sizeof(addr), pss); 123 d_printf("Looking up status of %s\n",addr); 124 make_nmb_name(&nname, name, type); 125 status = node_status_query(fd, &nname, pss, &count, &extra, 2000); 126 if (status) { 127 for (i=0;i<count;i++) { 128 pull_ascii_fstring(cleanname, status[i].name); 129 for (j=0;cleanname[j];j++) { 130 if (!isprint((int)cleanname[j])) { 131 cleanname[j] = '.'; 132 } 133 } 134 d_printf("\t%-15s <%02x> - %s\n", 135 cleanname,status[i].type, 136 node_status_flags(status[i].flags)); 137 } 138 d_printf("\n\tMAC Address = %02X-%02X-%02X-%02X-%02X-%02X\n", 139 extra.mac_addr[0], extra.mac_addr[1], 140 extra.mac_addr[2], extra.mac_addr[3], 141 extra.mac_addr[4], extra.mac_addr[5]); 142 d_printf("\n"); 143 SAFE_FREE(status); 144 } else { 145 d_printf("No reply from %s\n\n",addr); 146 } 147} 148 149 150/**************************************************************************** 151 Send out one query. 152****************************************************************************/ 153 154static bool query_one(const char *lookup, unsigned int lookup_type) 155{ 156 int j, count, flags = 0; 157 struct sockaddr_storage *ip_list=NULL; 158 159 if (got_bcast) { 160 char addr[INET6_ADDRSTRLEN]; 161 print_sockaddr(addr, sizeof(addr), &bcast_addr); 162 d_printf("querying %s on %s\n", lookup, addr); 163 ip_list = name_query(ServerFD,lookup,lookup_type,use_bcast, 164 use_bcast?true:recursion_desired, 165 &bcast_addr, &count, &flags, NULL); 166 } else { 167 const struct in_addr *bcast; 168 for (j=iface_count() - 1; 169 !ip_list && j >= 0; 170 j--) { 171 char addr[INET6_ADDRSTRLEN]; 172 struct sockaddr_storage bcast_ss; 173 174 bcast = iface_n_bcast_v4(j); 175 if (!bcast) { 176 continue; 177 } 178 in_addr_to_sockaddr_storage(&bcast_ss, *bcast); 179 print_sockaddr(addr, sizeof(addr), &bcast_ss); 180 d_printf("querying %s on %s\n", 181 lookup, addr); 182 ip_list = name_query(ServerFD,lookup,lookup_type, 183 use_bcast, 184 use_bcast?True:recursion_desired, 185 &bcast_ss,&count, &flags, NULL); 186 } 187 } 188 189 if (!ip_list) { 190 return false; 191 } 192 193 if (give_flags) { 194 d_printf("Flags: %s\n", query_flags(flags)); 195 } 196 197 for (j=0;j<count;j++) { 198 char addr[INET6_ADDRSTRLEN]; 199 if (translate_addresses) { 200 char h_name[MAX_DNS_NAME_LENGTH]; 201 h_name[0] = '\0'; 202 if (sys_getnameinfo((const struct sockaddr *)&ip_list[j], 203 sizeof(struct sockaddr_storage), 204 h_name, sizeof(h_name), 205 NULL, 0, 206 NI_NAMEREQD)) { 207 continue; 208 } 209 d_printf("%s, ", h_name); 210 } 211 print_sockaddr(addr, sizeof(addr), &ip_list[j]); 212 d_printf("%s %s<%02x>\n", addr,lookup, lookup_type); 213 /* We can only do find_status if the ip address returned 214 was valid - ie. name_query returned true. 215 */ 216 if (find_status) { 217 do_node_status(ServerFD, lookup, 218 lookup_type, &ip_list[j]); 219 } 220 } 221 222 free(ip_list); 223 224 return (ip_list != NULL); 225} 226 227 228/**************************************************************************** 229 main program 230****************************************************************************/ 231int main(int argc,char *argv[]) 232{ 233 int opt; 234 unsigned int lookup_type = 0x0; 235 fstring lookup; 236 static bool find_master=False; 237 static bool lookup_by_ip = False; 238 poptContext pc; 239 TALLOC_CTX *frame = talloc_stackframe(); 240 241 struct poptOption long_options[] = { 242 POPT_AUTOHELP 243 { "broadcast", 'B', POPT_ARG_STRING, NULL, 'B', "Specify address to use for broadcasts", "BROADCAST-ADDRESS" }, 244 { "flags", 'f', POPT_ARG_NONE, NULL, 'f', "List the NMB flags returned" }, 245 { "unicast", 'U', POPT_ARG_STRING, NULL, 'U', "Specify address to use for unicast" }, 246 { "master-browser", 'M', POPT_ARG_NONE, NULL, 'M', "Search for a master browser" }, 247 { "recursion", 'R', POPT_ARG_NONE, NULL, 'R', "Set recursion desired in package" }, 248 { "status", 'S', POPT_ARG_NONE, NULL, 'S', "Lookup node status as well" }, 249 { "translate", 'T', POPT_ARG_NONE, NULL, 'T', "Translate IP addresses into names" }, 250 { "root-port", 'r', POPT_ARG_NONE, NULL, 'r', "Use root port 137 (Win95 only replies to this)" }, 251 { "lookup-by-ip", 'A', POPT_ARG_NONE, NULL, 'A', "Do a node status on <name> as an IP Address" }, 252 POPT_COMMON_SAMBA 253 POPT_COMMON_CONNECTION 254 { 0, 0, 0, 0 } 255 }; 256 257 *lookup = 0; 258 259 load_case_tables(); 260 261 setup_logging(argv[0],True); 262 263 pc = poptGetContext("nmblookup", argc, (const char **)argv, 264 long_options, POPT_CONTEXT_KEEP_FIRST); 265 266 poptSetOtherOptionHelp(pc, "<NODE> ..."); 267 268 while ((opt = poptGetNextOpt(pc)) != -1) { 269 switch (opt) { 270 case 'f': 271 give_flags = true; 272 break; 273 case 'M': 274 find_master = true; 275 break; 276 case 'R': 277 recursion_desired = true; 278 break; 279 case 'S': 280 find_status = true; 281 break; 282 case 'r': 283 RootPort = true; 284 break; 285 case 'A': 286 lookup_by_ip = true; 287 break; 288 case 'B': 289 if (interpret_string_addr(&bcast_addr, 290 poptGetOptArg(pc), 291 NI_NUMERICHOST)) { 292 got_bcast = True; 293 use_bcast = True; 294 } 295 break; 296 case 'U': 297 if (interpret_string_addr(&bcast_addr, 298 poptGetOptArg(pc), 299 0)) { 300 got_bcast = True; 301 use_bcast = False; 302 } 303 break; 304 case 'T': 305 translate_addresses = !translate_addresses; 306 break; 307 } 308 } 309 310 poptGetArg(pc); /* Remove argv[0] */ 311 312 if(!poptPeekArg(pc)) { 313 poptPrintUsage(pc, stderr, 0); 314 exit(1); 315 } 316 317 if (!lp_load(get_dyn_CONFIGFILE(),True,False,False,True)) { 318 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 319 get_dyn_CONFIGFILE()); 320 } 321 322 load_interfaces(); 323 if (!open_sockets()) { 324 return(1); 325 } 326 327 while(poptPeekArg(pc)) { 328 char *p; 329 struct in_addr ip; 330 331 fstrcpy(lookup,poptGetArg(pc)); 332 333 if(lookup_by_ip) { 334 struct sockaddr_storage ss; 335 ip = interpret_addr2(lookup); 336 in_addr_to_sockaddr_storage(&ss, ip); 337 fstrcpy(lookup,"*"); 338 do_node_status(ServerFD, lookup, lookup_type, &ss); 339 continue; 340 } 341 342 if (find_master) { 343 if (*lookup == '-') { 344 fstrcpy(lookup,"\01\02__MSBROWSE__\02"); 345 lookup_type = 1; 346 } else { 347 lookup_type = 0x1d; 348 } 349 } 350 351 p = strchr_m(lookup,'#'); 352 if (p) { 353 *p = '\0'; 354 sscanf(++p,"%x",&lookup_type); 355 } 356 357 if (!query_one(lookup, lookup_type)) { 358 d_printf( "name_query failed to find name %s", lookup ); 359 if( 0 != lookup_type ) { 360 d_printf( "#%02x", lookup_type ); 361 } 362 d_printf( "\n" ); 363 } 364 } 365 366 poptFreeContext(pc); 367 TALLOC_FREE(frame); 368 return(0); 369} 370