diff options
Diffstat (limited to 'source/libsmb/namequery.c')
-rw-r--r-- | source/libsmb/namequery.c | 575 |
1 files changed, 575 insertions, 0 deletions
diff --git a/source/libsmb/namequery.c b/source/libsmb/namequery.c new file mode 100644 index 00000000000..f988504bba5 --- /dev/null +++ b/source/libsmb/namequery.c @@ -0,0 +1,575 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + 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" + +extern pstring scope; +extern int DEBUGLEVEL; + +/* nmbd.c sets this to True. */ +BOOL global_in_nmbd = False; + +/**************************************************************************** +interpret a node status response +****************************************************************************/ +static void _interpret_node_status(char *p, char *master,char *rname) +{ + int numnames = CVAL(p,0); + DEBUG(1,("received %d names\n",numnames)); + + if (rname) *rname = 0; + if (master) *master = 0; + + p += 1; + while (numnames--) + { + char qname[17]; + int type; + fstring flags; + int i; + *flags = 0; + StrnCpy(qname,p,15); + type = CVAL(p,15); + p += 16; + + fstrcat(flags, (p[0] & 0x80) ? "<GROUP> " : " "); + if ((p[0] & 0x60) == 0x00) fstrcat(flags,"B "); + if ((p[0] & 0x60) == 0x20) fstrcat(flags,"P "); + if ((p[0] & 0x60) == 0x40) fstrcat(flags,"M "); + if ((p[0] & 0x60) == 0x60) fstrcat(flags,"H "); + if (p[0] & 0x10) fstrcat(flags,"<DEREGISTERING> "); + if (p[0] & 0x08) fstrcat(flags,"<CONFLICT> "); + if (p[0] & 0x04) fstrcat(flags,"<ACTIVE> "); + if (p[0] & 0x02) fstrcat(flags,"<PERMANENT> "); + + if (master && !*master && type == 0x1d) { + StrnCpy(master,qname,15); + trim_string(master,NULL," "); + } + + if (rname && !*rname && type == 0x20 && !(p[0]&0x80)) { + StrnCpy(rname,qname,15); + trim_string(rname,NULL," "); + } + + for (i = strlen( qname) ; --i >= 0 ; ) { + if (!isprint((int)qname[i])) qname[i] = '.'; + } + DEBUG(1,("\t%-15s <%02x> - %s\n",qname,type,flags)); + p+=2; + } + DEBUG(1,("num_good_sends=%d num_good_receives=%d\n", + IVAL(p,20),IVAL(p,24))); +} + + +/**************************************************************************** + do a netbios name status query on a host + + the "master" parameter is a hack used for finding workgroups. + **************************************************************************/ +BOOL name_status(int fd,char *name,int name_type,BOOL recurse, + struct in_addr to_ip,char *master,char *rname, + void (*fn)(struct packet_struct *)) +{ + BOOL found=False; + int retries = 2; + int retry_time = 5000; + struct timeval tval; + struct packet_struct p; + struct packet_struct *p2; + struct nmb_packet *nmb = &p.packet.nmb; + static int name_trn_id = 0; + + bzero((char *)&p,sizeof(p)); + + if (!name_trn_id) name_trn_id = ((unsigned)time(NULL)%(unsigned)0x7FFF) + + ((unsigned)getpid()%(unsigned)100); + name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; + + nmb->header.name_trn_id = name_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; + + make_nmb_name(&nmb->question.question_name,name,name_type,scope); + + 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(False); + + retries--; + + while (1) + { + struct timeval tval2; + GetTimeOfDay(&tval2); + if (TvalDiff(&tval,&tval2) > retry_time) { + if (!retries) break; + if (!found && !send_packet(&p)) + return False; + GetTimeOfDay(&tval); + retries--; + } + + if ((p2=receive_packet(fd,NMB_PACKET,90))) + { + struct nmb_packet *nmb2 = &p2->packet.nmb; + debug_nmb_packet(p2); + + if (nmb->header.name_trn_id != nmb2->header.name_trn_id || + !nmb2->header.response) { + /* its not for us - maybe deal with it later */ + if (fn) + fn(p2); + else + free_packet(p2); + continue; + } + + 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; + } + + _interpret_node_status(&nmb2->answers->rdata[0], master,rname); + free_packet(p2); + return(True); + } + } + + + DEBUG(0,("No status response (this is not unusual)\n")); + + return(False); +} + + +/**************************************************************************** + 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 + ****************************************************************************/ +struct in_addr *name_query(int fd,char *name,int name_type, BOOL bcast,BOOL recurse, + struct in_addr to_ip, int *count, void (*fn)(struct packet_struct *)) +{ + 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; + static int name_trn_id = 0; + struct in_addr *ip_list = NULL; + + bzero((char *)&p,sizeof(p)); + (*count) = 0; + + if (!name_trn_id) name_trn_id = ((unsigned)time(NULL)%(unsigned)0x7FFF) + + ((unsigned)getpid()%(unsigned)100); + name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; + + nmb->header.name_trn_id = name_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,scope); + + 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; + GetTimeOfDay(&tval2); + if (TvalDiff(&tval,&tval2) > retry_time) { + if (!retries) break; + if (!found && !send_packet(&p)) + return NULL; + GetTimeOfDay(&tval); + retries--; + } + + if ((p2=receive_packet(fd,NMB_PACKET,90))) + { + struct nmb_packet *nmb2 = &p2->packet.nmb; + debug_nmb_packet(p2); + + if (nmb->header.name_trn_id != nmb2->header.name_trn_id || + !nmb2->header.response) { + /* its not for us - maybe deal with it later + (put it on the queue?) */ + if (fn) + fn(p2); + else + free_packet(p2); + continue; + } + + 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; + } + + ip_list = (struct in_addr *)Realloc(ip_list, sizeof(ip_list[0]) * + ((*count)+nmb2->answers->rdlength/6)); + if (ip_list) { + DEBUG(fn?3:2,("Got a positive name query response from %s ( ", + inet_ntoa(p2->ip))); + for (i=0;i<nmb2->answers->rdlength/6;i++) { + putip((char *)&ip_list[(*count)],&nmb2->answers->rdata[2+i*6]); + DEBUG(fn?3:2,("%s ",inet_ntoa(ip_list[(*count)]))); + (*count)++; + } + DEBUG(fn?3:2,(")\n")); + } + found=True; retries=0; + free_packet(p2); + if (fn) break; + } + } + + return ip_list; +} + +/******************************************************** + Start parsing the lmhosts file. +*********************************************************/ + +FILE *startlmhosts(char *fname) +{ + FILE *fp = fopen(fname,"r"); + if (!fp) { + DEBUG(4,("startlmhosts: Can't open lmhosts file %s. Error was %s\n", + fname, strerror(errno))); + return NULL; + } + return fp; +} + +/******************************************************** + Parse the next line in the lmhosts file. +*********************************************************/ + +BOOL getlmhostsent( FILE *fp, char *name, int *name_type, struct in_addr *ipaddr) +{ + pstring line; + + while(!feof(fp) && !ferror(fp)) { + pstring ip,flags,extra; + char *ptr; + int count = 0; + + *name_type = -1; + + if (!fgets_slash(line,sizeof(pstring),fp)) + continue; + + if (*line == '#') + continue; + + pstrcpy(ip,""); + pstrcpy(name,""); + pstrcpy(flags,""); + + ptr = line; + + if (next_token(&ptr,ip ,NULL,sizeof(ip))) + ++count; + if (next_token(&ptr,name ,NULL, sizeof(name))) + ++count; + if (next_token(&ptr,flags,NULL, sizeof(flags))) + ++count; + if (next_token(&ptr,extra,NULL, sizeof(extra))) + ++count; + + if (count <= 0) + continue; + + if (count > 0 && count < 2) + { + DEBUG(0,("getlmhostsent: Ill formed hosts line [%s]\n",line)); + continue; + } + + if (count >= 4) + { + DEBUG(0,("getlmhostsent: too many columns in lmhosts file (obsolete syntax)\n")); + continue; + } + + DEBUG(4, ("getlmhostsent: lmhost entry: %s %s %s\n", ip, name, flags)); + + if (strchr(flags,'G') || strchr(flags,'S')) + { + DEBUG(0,("getlmhostsent: group flag in lmhosts ignored (obsolete)\n")); + continue; + } + + *ipaddr = *interpret_addr2(ip); + + /* Extra feature. If the name ends in '#XX', where XX is a hex number, + then only add that name type. */ + if((ptr = strchr(name, '#')) != NULL) + { + char *endptr; + + ptr++; + *name_type = (int)strtol(ptr, &endptr,0); + + if(!*ptr || (endptr == ptr)) + { + DEBUG(0,("getlmhostsent: invalid name %s containing '#'.\n", name)); + continue; + } + + *(--ptr) = '\0'; /* Truncate at the '#' */ + } + + return True; + } + + return False; +} + +/******************************************************** + Finish parsing the lmhosts file. +*********************************************************/ + +void endlmhosts(FILE *fp) +{ + fclose(fp); +} + +/******************************************************** + Resolve a name into an IP address. Use this function if + the string is either an IP address, DNS or host name + or NetBIOS name. This uses the name switch in the + smb.conf to determine the order of name resolution. +*********************************************************/ + +BOOL resolve_name(char *name, struct in_addr *return_ip) +{ + int i; + BOOL pure_address = True; + pstring name_resolve_list; + fstring tok; + char *ptr; + + if (strcmp(name,"0.0.0.0") == 0) { + return_ip->s_addr = 0; + return True; + } + if (strcmp(name,"255.255.255.255") == 0) { + return_ip->s_addr = 0xFFFFFFFF; + return True; + } + + for (i=0; pure_address && name[i]; i++) + if (!(isdigit((int)name[i]) || name[i] == '.')) + pure_address = False; + + /* if it's in the form of an IP address then get the lib to interpret it */ + if (pure_address) { + return_ip->s_addr = inet_addr(name); + return True; + } + + pstrcpy(name_resolve_list, lp_name_resolve_order()); + ptr = name_resolve_list; + if (!ptr || !*ptr) ptr = "host"; + + while (next_token(&ptr, tok, LIST_SEP, sizeof(tok))) { + if(strequal(tok, "host") || strequal(tok, "hosts")) { + + /* + * "host" means do a localhost, or dns lookup. + */ + + struct hostent *hp; + + DEBUG(3,("resolve_name: Attempting host lookup for name %s\n", name)); + + if (((hp = Get_Hostbyname(name)) != NULL) && (hp->h_addr != NULL)) { + putip((char *)return_ip,(char *)hp->h_addr); + return True; + } + + } else if(strequal( tok, "lmhosts")) { + + /* + * "lmhosts" means parse the local lmhosts file. + */ + + FILE *fp; + pstring lmhost_name; + int name_type; + + DEBUG(3,("resolve_name: Attempting lmhosts lookup for name %s\n", name)); + + fp = startlmhosts( LMHOSTSFILE ); + if(fp) { + while( getlmhostsent(fp, lmhost_name, &name_type, return_ip ) ) { + if( strequal(name, lmhost_name )) { + endlmhosts(fp); + return True; + } + } + endlmhosts(fp); + } + + } else if(strequal( tok, "wins")) { + + int sock; + + /* + * "wins" means do a unicast lookup to the WINS server. + * Ignore if there is no WINS server specified or if the + * WINS server is one of our interfaces (if we're being + * called from within nmbd - we can't do this call as we + * would then block). + */ + + DEBUG(3,("resolve_name: Attempting wins lookup for name %s<0x20>\n", name)); + + if(*lp_wins_server()) { + struct in_addr wins_ip = *interpret_addr2(lp_wins_server()); + BOOL wins_ismyip = ismyip(wins_ip); + + if((wins_ismyip && !global_in_nmbd) || !wins_ismyip) { + sock = open_socket_in( SOCK_DGRAM, 0, 3, + interpret_addr(lp_socket_address()) ); + + if (sock != -1) { + struct in_addr *iplist = NULL; + int count; + iplist = name_query(sock, name, 0x20, False, True, wins_ip, &count, NULL); + if(iplist != NULL) { + *return_ip = iplist[0]; + free((char *)iplist); + close(sock); + return True; + } + close(sock); + } + } + } else { + DEBUG(3,("resolve_name: WINS server resolution selected and no WINS server present.\n")); + } + } else if(strequal( tok, "bcast")) { + + int sock; + + /* + * "bcast" means do a broadcast lookup on all the local interfaces. + */ + + DEBUG(3,("resolve_name: Attempting broadcast lookup for name %s<0x20>\n", name)); + + sock = open_socket_in( SOCK_DGRAM, 0, 3, + interpret_addr(lp_socket_address()) ); + + if (sock != -1) { + struct in_addr *iplist = NULL; + int count; + int num_interfaces = iface_count(); + set_socket_options(sock,"SO_BROADCAST"); + /* + * Lookup the name on all the interfaces, return on + * the first successful match. + */ + for( i = 0; i < num_interfaces; i++) { + struct in_addr sendto_ip; + /* Done this way to fix compiler error on IRIX 5.x */ + sendto_ip = *iface_bcast(*iface_n_ip(i)); + iplist = name_query(sock, name, 0x20, True, False, sendto_ip, &count, NULL); + if(iplist != NULL) { + *return_ip = iplist[0]; + free((char *)iplist); + close(sock); + return True; + } + } + close(sock); + } + + } else { + DEBUG(0,("resolve_name: unknown name switch type %s\n", tok)); + } + } + + return False; +} |