📄 namequery.c
字号:
/* Unix SMB/CIFS implementation. name query routines Copyright (C) Andrew Tridgell 1994-1998 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 (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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. */#include "includes.h"/* nmbd.c sets this to True. */BOOL global_in_nmbd = False;/**************************************************************************** Generate a random trn_id.****************************************************************************/static int generate_trn_id(void){ static int trn_id; if (trn_id == 0) { sys_srandom(sys_getpid()); } trn_id = sys_random(); return trn_id % (unsigned)0x7FFF;}/**************************************************************************** Parse a node status response into an array of structures.****************************************************************************/static NODE_STATUS_STRUCT *parse_node_status(char *p, int *num_names, struct node_status_extra *extra){ NODE_STATUS_STRUCT *ret; int i; *num_names = CVAL(p,0); if (*num_names == 0) return NULL; ret = SMB_MALLOC_ARRAY(NODE_STATUS_STRUCT,*num_names); if (!ret) return NULL; p++; for (i=0;i< *num_names;i++) { StrnCpy(ret[i].name,p,15); trim_char(ret[i].name,'\0',' '); ret[i].type = CVAL(p,15); ret[i].flags = p[16]; p += 18; DEBUG(10, ("%s#%02x: flags = 0x%02x\n", ret[i].name, ret[i].type, ret[i].flags)); } /* * Also, pick up the MAC address ... */ if (extra) { memcpy(&extra->mac_addr, p, 6); /* Fill in the mac addr */ } return ret;}/**************************************************************************** Do a NBT node status query on an open socket and return an array of structures holding the returned names or NULL if the query failed.**************************************************************************/NODE_STATUS_STRUCT *node_status_query(int fd,struct nmb_name *name, struct in_addr to_ip, int *num_names, struct node_status_extra *extra){ BOOL found=False; int retries = 2; int retry_time = 2000; struct timeval tval; struct packet_struct p; struct packet_struct *p2; struct nmb_packet *nmb = &p.packet.nmb; NODE_STATUS_STRUCT *ret; ZERO_STRUCT(p); nmb->header.name_trn_id = generate_trn_id(); nmb->header.opcode = 0; nmb->header.response = False; nmb->header.nm_flags.bcast = False; nmb->header.nm_flags.recursion_available = False; nmb->header.nm_flags.recursion_desired = False; nmb->header.nm_flags.trunc = False; nmb->header.nm_flags.authoritative = False; nmb->header.rcode = 0; nmb->header.qdcount = 1; nmb->header.ancount = 0; nmb->header.nscount = 0; nmb->header.arcount = 0; nmb->question.question_name = *name; nmb->question.question_type = 0x21; nmb->question.question_class = 0x1; p.ip = to_ip; p.port = NMB_PORT; p.fd = fd; p.timestamp = time(NULL); p.packet_type = NMB_PACKET; GetTimeOfDay(&tval); if (!send_packet(&p)) return NULL; retries--; while (1) { struct timeval tval2; GetTimeOfDay(&tval2); if (TvalDiff(&tval,&tval2) > retry_time) { if (!retries) break; if (!found && !send_packet(&p)) return NULL; GetTimeOfDay(&tval); retries--; } if ((p2=receive_nmb_packet(fd,90,nmb->header.name_trn_id))) { struct nmb_packet *nmb2 = &p2->packet.nmb; debug_nmb_packet(p2); if (nmb2->header.opcode != 0 || nmb2->header.nm_flags.bcast || nmb2->header.rcode || !nmb2->header.ancount || nmb2->answers->rr_type != 0x21) { /* XXXX what do we do with this? could be a redirect, but we'll discard it for the moment */ free_packet(p2); continue; } ret = parse_node_status(&nmb2->answers->rdata[0], num_names, extra); free_packet(p2); return ret; } } return NULL;}/**************************************************************************** Find the first type XX name in a node status reply - used for finding a servers name given its IP. Return the matched name in *name.**************************************************************************/BOOL name_status_find(const char *q_name, int q_type, int type, struct in_addr to_ip, fstring name){ NODE_STATUS_STRUCT *status = NULL; struct nmb_name nname; int count, i; int sock; BOOL result = False; if (lp_disable_netbios()) { DEBUG(5,("name_status_find(%s#%02x): netbios is disabled\n", q_name, q_type)); return False; } DEBUG(10, ("name_status_find: looking up %s#%02x at %s\n", q_name, q_type, inet_ntoa(to_ip))); /* Check the cache first. */ if (namecache_status_fetch(q_name, q_type, type, to_ip, name)) return True; sock = open_socket_in(SOCK_DGRAM, 0, 3, interpret_addr(lp_socket_address()), True); if (sock == -1) goto done; /* W2K PDC's seem not to respond to '*'#0. JRA */ make_nmb_name(&nname, q_name, q_type); status = node_status_query(sock, &nname, to_ip, &count, NULL); close(sock); if (!status) goto done; for (i=0;i<count;i++) { if (status[i].type == type) break; } if (i == count) goto done; pull_ascii_nstring(name, sizeof(fstring), status[i].name); /* Store the result in the cache. */ /* but don't store an entry for 0x1c names here. Here we have a single host and DOMAIN<0x1c> names should be a list of hosts */ if ( q_type != 0x1c ) namecache_status_store(q_name, q_type, type, to_ip, name); result = True; done: SAFE_FREE(status); DEBUG(10, ("name_status_find: name %sfound", result ? "" : "not ")); if (result) DEBUGADD(10, (", name %s ip address is %s", name, inet_ntoa(to_ip))); DEBUG(10, ("\n")); return result;}/* comparison function used by sort_ip_list*/static int ip_compare(struct in_addr *ip1, struct in_addr *ip2){ int max_bits1=0, max_bits2=0; int num_interfaces = iface_count(); int i; for (i=0;i<num_interfaces;i++) { struct in_addr ip; int bits1, bits2; ip = *iface_n_bcast(i); bits1 = matching_quad_bits((uchar *)&ip1->s_addr, (uchar *)&ip.s_addr); bits2 = matching_quad_bits((uchar *)&ip2->s_addr, (uchar *)&ip.s_addr); max_bits1 = MAX(bits1, max_bits1); max_bits2 = MAX(bits2, max_bits2); } /* bias towards directly reachable IPs */ if (iface_local(*ip1)) { max_bits1 += 32; } if (iface_local(*ip2)) { max_bits2 += 32; } return max_bits2 - max_bits1;}/******************************************************************* compare 2 ldap IPs by nearness to our interfaces - used in qsort*******************************************************************/static int ip_service_compare(struct ip_service *ip1, struct ip_service *ip2){ int result; if ( (result = ip_compare(&ip1->ip, &ip2->ip)) != 0 ) return result; if ( ip1->port > ip2->port ) return 1; if ( ip1->port < ip2->port ) return -1; return 0;}/* sort an IP list so that names that are close to one of our interfaces are at the top. This prevents the problem where a WINS server returns an IP that is not reachable from our subnet as the first match*/static void sort_ip_list(struct in_addr *iplist, int count){ if (count <= 1) { return; } qsort(iplist, count, sizeof(struct in_addr), QSORT_CAST ip_compare); }static void sort_ip_list2(struct ip_service *iplist, int count){ if (count <= 1) { return; } qsort(iplist, count, sizeof(struct ip_service), QSORT_CAST ip_service_compare); }/********************************************************************** Remove any duplicate address/port pairs in the list *********************************************************************/static int remove_duplicate_addrs2( struct ip_service *iplist, int count ){ int i, j; DEBUG(10,("remove_duplicate_addrs2: looking for duplicate address/port pairs\n")); /* one loop to remove duplicates */ for ( i=0; i<count; i++ ) { if ( is_zero_ip(iplist[i].ip) ) continue; for ( j=i+1; j<count; j++ ) { if ( ip_service_equal(iplist[i], iplist[j]) ) zero_ip(&iplist[j].ip); } } /* one loop to clean up any holes we left */ /* first ip should never be a zero_ip() */ for (i = 0; i<count; ) { if ( is_zero_ip(iplist[i].ip) ) { if (i != count-1 ) memmove(&iplist[i], &iplist[i+1], (count - i - 1)*sizeof(iplist[i])); count--; continue; } i++; } return count;}/**************************************************************************** Do a netbios name query to find someones IP. Returns an array of IP addresses or NULL if none. *count will be set to the number of addresses returned. *timed_out is set if we failed by timing out****************************************************************************/struct in_addr *name_query(int fd,const char *name,int name_type, BOOL bcast,BOOL recurse, struct in_addr to_ip, int *count, int *flags, BOOL *timed_out){ BOOL found=False; int i, retries = 3; int retry_time = bcast?250:2000; struct timeval tval; struct packet_struct p; struct packet_struct *p2; struct nmb_packet *nmb = &p.packet.nmb; struct in_addr *ip_list = NULL; if (lp_disable_netbios()) { DEBUG(5,("name_query(%s#%02x): netbios is disabled\n", name, name_type)); return NULL; } if (timed_out) { *timed_out = False; } memset((char *)&p,'\0',sizeof(p)); (*count) = 0; (*flags) = 0; nmb->header.name_trn_id = generate_trn_id(); nmb->header.opcode = 0; nmb->header.response = False; nmb->header.nm_flags.bcast = bcast; nmb->header.nm_flags.recursion_available = False; nmb->header.nm_flags.recursion_desired = recurse; nmb->header.nm_flags.trunc = False; nmb->header.nm_flags.authoritative = False; nmb->header.rcode = 0; nmb->header.qdcount = 1; nmb->header.ancount = 0; nmb->header.nscount = 0; nmb->header.arcount = 0; make_nmb_name(&nmb->question.question_name,name,name_type); nmb->question.question_type = 0x20; nmb->question.question_class = 0x1; p.ip = to_ip; p.port = NMB_PORT; p.fd = fd; p.timestamp = time(NULL); p.packet_type = NMB_PACKET; GetTimeOfDay(&tval); if (!send_packet(&p)) return NULL; retries--; while (1) { struct timeval tval2; struct in_addr *tmp_ip_list; GetTimeOfDay(&tval2); if (TvalDiff(&tval,&tval2) > retry_time) { if (!retries) break; if (!found && !send_packet(&p)) return NULL; GetTimeOfDay(&tval); retries--; } if ((p2=receive_nmb_packet(fd,90,nmb->header.name_trn_id))) { struct nmb_packet *nmb2 = &p2->packet.nmb; debug_nmb_packet(p2); /* If we get a Negative Name Query Response from a WINS * server, we should report it and give up. */ if( 0 == nmb2->header.opcode /* A query response */ && !(bcast) /* from a WINS server */ && nmb2->header.rcode /* Error returned */ ) { if( DEBUGLVL( 3 ) ) { /* Only executed if DEBUGLEVEL >= 3 */ dbgtext( "Negative name query response, rcode 0x%02x: ", nmb2->header.rcode ); switch( nmb2->header.rcode ) { case 0x01: dbgtext( "Request was invalidly formatted.\n" ); break; case 0x02: dbgtext( "Problem with NBNS, cannot process name.\n"); break; case 0x03: dbgtext( "The name requested does not exist.\n" ); break; case 0x04: dbgtext( "Unsupported request error.\n" ); break; case 0x05: dbgtext( "Query refused error.\n" ); break; default: dbgtext( "Unrecognized error code.\n" ); break; } } free_packet(p2); return( NULL ); } if (nmb2->header.opcode != 0 || nmb2->header.nm_flags.bcast || nmb2->header.rcode || !nmb2->header.ancount) { /* * XXXX what do we do with this? Could be a * redirect, but we'll discard it for the * moment. */ free_packet(p2); continue; } tmp_ip_list = SMB_REALLOC_ARRAY( ip_list, struct in_addr, (*count) + nmb2->answers->rdlength/6 );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -