jeremy is going to hate me for this.
[tprouty/samba.git] / source / libsmb / namequery.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    name query routines
5    Copyright (C) Andrew Tridgell 1994-1998
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 2 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, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21 */
22
23 #include "includes.h"
24
25 extern pstring scope;
26 extern pstring global_myname;
27 extern int DEBUGLEVEL;
28
29 /* nmbd.c sets this to True. */
30 BOOL global_in_nmbd = False;
31
32   static int name_trn_id = 0;
33
34 /****************************************************************************
35 interpret a node status response
36 ****************************************************************************/
37 static void _interpret_node_status(char *p, char *master,char *rname)
38 {
39   int numnames = CVAL(p,0);
40   DEBUG(1,("received %d names\n",numnames));
41
42   if (rname) *rname = 0;
43   if (master) *master = 0;
44
45   p += 1;
46   while (numnames--)
47     {
48       char qname[17];
49       int type;
50       fstring flags;
51       int i;
52       *flags = 0;
53       StrnCpy(qname,p,15);
54       type = CVAL(p,15);
55       p += 16;
56
57       fstrcat(flags, (p[0] & 0x80) ? "<GROUP> " : "        ");
58       if ((p[0] & 0x60) == 0x00) fstrcat(flags,"B ");
59       if ((p[0] & 0x60) == 0x20) fstrcat(flags,"P ");
60       if ((p[0] & 0x60) == 0x40) fstrcat(flags,"M ");
61       if ((p[0] & 0x60) == 0x60) fstrcat(flags,"H ");
62       if (p[0] & 0x10) fstrcat(flags,"<DEREGISTERING> ");
63       if (p[0] & 0x08) fstrcat(flags,"<CONFLICT> ");
64       if (p[0] & 0x04) fstrcat(flags,"<ACTIVE> ");
65       if (p[0] & 0x02) fstrcat(flags,"<PERMANENT> ");
66
67       if (master && !*master && type == 0x1d) {
68         StrnCpy(master,qname,15);
69         trim_string(master,NULL," ");
70       }
71
72       if (rname && !*rname && type == 0x20 && !(p[0]&0x80)) {
73         StrnCpy(rname,qname,15);
74         trim_string(rname,NULL," ");
75       }
76       
77       for (i = strlen( qname) ; --i >= 0 ; ) {
78         if (!isprint((int)qname[i])) qname[i] = '.';
79       }
80       DEBUG(1,("\t%-15s <%02x> - %s\n",qname,type,flags));
81       p+=2;
82     }
83   DEBUG(1,("num_good_sends=%d num_good_receives=%d\n",
84                IVAL(p,20),IVAL(p,24)));
85 }
86
87
88 /****************************************************************************
89   do a netbios name status query on a host
90
91   the "master" parameter is a hack used for finding workgroups.
92   **************************************************************************/
93 BOOL name_status(int fd,char *name,int name_type,BOOL recurse,
94                  struct in_addr to_ip,char *master,char *rname,
95                  void (*fn)(struct packet_struct *))
96 {
97   BOOL found=False;
98   int retries = 2;
99   int retry_time = 5000;
100   struct timeval tval;
101   struct packet_struct p;
102   struct packet_struct *p2;
103   struct nmb_packet *nmb = &p.packet.nmb;
104         int packet_type = NMB_PACKET;
105
106         if (fd == -1)
107         {
108                 retries = 1;
109                 packet_type = NMB_SOCK_PACKET;
110                 fd = get_nmb_sock();
111
112                 if (fd < 0)
113                 {
114                         return False;
115                 }
116         }
117   bzero((char *)&p,sizeof(p));
118
119   if (!name_trn_id) name_trn_id = ((unsigned)time(NULL)%(unsigned)0x7FFF) + 
120     ((unsigned)getpid()%(unsigned)100);
121   name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
122
123   nmb->header.name_trn_id = name_trn_id;
124   nmb->header.opcode = 0;
125   nmb->header.response = False;
126   nmb->header.nm_flags.bcast = False;
127   nmb->header.nm_flags.recursion_available = False;
128   nmb->header.nm_flags.recursion_desired = False;
129   nmb->header.nm_flags.trunc = False;
130   nmb->header.nm_flags.authoritative = False;
131   nmb->header.rcode = 0;
132   nmb->header.qdcount = 1;
133   nmb->header.ancount = 0;
134   nmb->header.nscount = 0;
135   nmb->header.arcount = 0;
136
137   make_nmb_name(&nmb->question.question_name,name,name_type,scope);
138
139   nmb->question.question_type = 0x21;
140   nmb->question.question_class = 0x1;
141
142   p.ip = to_ip;
143   p.port = NMB_PORT;
144   p.fd = fd;
145   p.timestamp = time(NULL);
146   p.packet_type = packet_type;
147
148   GetTimeOfDay(&tval);
149
150   if (!send_packet(&p)) 
151         {
152                 if (packet_type == NMB_SOCK_PACKET) close(fd);
153     return(False);
154         }
155
156   retries--;
157
158   while (1)
159     {
160       struct timeval tval2;
161       GetTimeOfDay(&tval2);
162       if (TvalDiff(&tval,&tval2) > retry_time) {
163         if (!retries) break;
164         if (!found && !send_packet(&p))
165         {
166                 if (packet_type == NMB_SOCK_PACKET) close(fd);
167           return False;
168         }
169         GetTimeOfDay(&tval);
170         retries--;
171       }
172
173       if ((p2=receive_packet(fd,packet_type,90)))
174         {     
175           struct nmb_packet *nmb2 = &p2->packet.nmb;
176       debug_nmb_packet(p2);
177
178           if (nmb->header.name_trn_id != nmb2->header.name_trn_id ||
179               !nmb2->header.response) {
180             /* its not for us - maybe deal with it later */
181             if (fn) 
182               fn(p2);
183             else
184               free_packet(p2);
185             continue;
186           }
187           
188           if (nmb2->header.opcode != 0 ||
189               nmb2->header.nm_flags.bcast ||
190               nmb2->header.rcode ||
191               !nmb2->header.ancount ||
192               nmb2->answers->rr_type != 0x21) {
193             /* XXXX what do we do with this? could be a redirect, but
194                we'll discard it for the moment */
195             free_packet(p2);
196             continue;
197           }
198
199           _interpret_node_status(&nmb2->answers->rdata[0], master,rname);
200           free_packet(p2);
201                 if (packet_type == NMB_SOCK_PACKET) close(fd);
202           return(True);
203         }
204     }
205   
206
207   DEBUG(0,("No status response (this is not unusual)\n"));
208
209 if (packet_type == NMB_SOCK_PACKET) close(fd);
210   return(False);
211 }
212
213
214 /****************************************************************************
215   do a netbios name query to find someones IP
216   returns an array of IP addresses or NULL if none
217   *count will be set to the number of addresses returned
218   ****************************************************************************/
219 struct in_addr *name_query(int fd,const char *name,int name_type, BOOL bcast,BOOL recurse,
220          struct in_addr to_ip, int *count, void (*fn)(struct packet_struct *))
221 {
222   BOOL found=False;
223   int i, retries = 3;
224   int retry_time = bcast?250:2000;
225   struct timeval tval;
226   struct packet_struct p;
227   struct packet_struct *p2;
228   struct nmb_packet *nmb = &p.packet.nmb;
229   struct in_addr *ip_list = NULL;
230         BOOL packet_type = NMB_PACKET;
231
232         if (fd == -1)
233         {
234                 retries = 0;
235                 packet_type = NMB_SOCK_PACKET;
236                 fd = get_nmb_sock();
237
238                 if (fd < 0)
239                 {
240                         return NULL;
241                 }
242         }
243
244   bzero((char *)&p,sizeof(p));
245   (*count) = 0;
246
247   if (!name_trn_id) name_trn_id = ((unsigned)time(NULL)%(unsigned)0x7FFF) + 
248     ((unsigned)getpid()%(unsigned)100);
249   name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
250
251   nmb->header.name_trn_id = name_trn_id;
252   nmb->header.opcode = 0;
253   nmb->header.response = False;
254   nmb->header.nm_flags.bcast = bcast;
255   nmb->header.nm_flags.recursion_available = False;
256   nmb->header.nm_flags.recursion_desired = recurse;
257   nmb->header.nm_flags.trunc = False;
258   nmb->header.nm_flags.authoritative = False;
259   nmb->header.rcode = 0;
260   nmb->header.qdcount = 1;
261   nmb->header.ancount = 0;
262   nmb->header.nscount = 0;
263   nmb->header.arcount = 0;
264
265   make_nmb_name(&nmb->question.question_name,name,name_type,scope);
266
267   nmb->question.question_type = 0x20;
268   nmb->question.question_class = 0x1;
269
270   p.ip = to_ip;
271   p.port = NMB_PORT;
272   p.fd = fd;
273   p.timestamp = time(NULL);
274   p.packet_type = packet_type;
275
276   GetTimeOfDay(&tval);
277
278   if (!send_packet(&p)) 
279         {       
280                 if (packet_type == NMB_SOCK_PACKET) close(fd);
281     return NULL;
282         }
283
284   while (retries >= 0)
285   {
286     struct timeval tval2;
287
288         retries--;
289
290     GetTimeOfDay(&tval2);
291     if (TvalDiff(&tval,&tval2) > retry_time) 
292     {
293       if (!found && !send_packet(&p))
294         {
295                 if (packet_type == NMB_SOCK_PACKET) close(fd);
296         return NULL;
297         }
298       GetTimeOfDay(&tval);
299     }
300
301     if ((p2=receive_packet(fd,packet_type,90)))
302     {     
303       struct nmb_packet *nmb2 = &p2->packet.nmb;
304       debug_nmb_packet(p2);
305
306       if (nmb->header.name_trn_id != nmb2->header.name_trn_id ||
307           !nmb2->header.response)
308       {
309         DEBUG(10,("packet not for us (received %d, expected %d\n",
310       nmb2->header.name_trn_id, nmb->header.name_trn_id));
311         /* 
312          * Its not for us - maybe deal with it later 
313          * (put it on the queue?).
314          */
315         if (fn) 
316           fn(p2);
317         else
318           free_packet(p2);
319         continue;
320       }
321           
322       if (nmb2->header.opcode != 0 ||
323           nmb2->header.nm_flags.bcast ||
324           nmb2->header.rcode ||
325           !nmb2->header.ancount)
326       {
327             /* 
328          * XXXX what do we do with this? Could be a redirect, but
329          * we'll discard it for the moment.
330          */
331         free_packet(p2);
332         continue;
333       }
334
335       ip_list = (struct in_addr *)Realloc(ip_list, sizeof(ip_list[0]) * 
336                                           ((*count)+nmb2->answers->rdlength/6));
337       if (ip_list)
338       {
339         DEBUG(fn?3:2,("Got a positive name query response from %s ( ",
340               inet_ntoa(p2->ip)));
341         for (i=0;i<nmb2->answers->rdlength/6;i++)
342         {
343           putip((char *)&ip_list[(*count)],&nmb2->answers->rdata[2+i*6]);
344           DEBUG(fn?3:2,("%s ",inet_ntoa(ip_list[(*count)])));
345           (*count)++;
346         }
347         DEBUG(fn?3:2,(")\n"));
348       }
349
350       found=True;
351       retries=0;
352       free_packet(p2);
353       if (fn)
354         break;
355
356       if(found)
357         {
358         DEBUG(10,("returning OK\n"));
359         break;
360         }
361     }
362   }
363
364         if (packet_type == NMB_SOCK_PACKET) close(fd);
365   return ip_list;
366 }
367
368 /********************************************************
369  Start parsing the lmhosts file.
370 *********************************************************/
371
372 FILE *startlmhosts(char *fname)
373 {
374   FILE *fp = sys_fopen(fname,"r");
375   if (!fp) {
376     DEBUG(4,("startlmhosts: Can't open lmhosts file %s. Error was %s\n",
377              fname, strerror(errno)));
378     return NULL;
379   }
380   return fp;
381 }
382
383 /********************************************************
384  Parse the next line in the lmhosts file.
385 *********************************************************/
386 BOOL getlmhostsent( FILE *fp, pstring name, int *name_type, struct in_addr *ipaddr)
387 {
388   pstring line;
389
390   while(!feof(fp) && !ferror(fp)) {
391     pstring ip,flags,extra;
392     char *ptr;
393     int count = 0;
394
395     *name_type = -1;
396
397     if (!fgets_slash(line,sizeof(pstring),fp))
398       continue;
399
400     if (*line == '#')
401       continue;
402
403     pstrcpy(ip,"");
404     pstrcpy(name,"");
405     pstrcpy(flags,"");
406
407     ptr = line;
408
409     if (next_token(&ptr,ip   ,NULL,sizeof(ip)))
410       ++count;
411     if (next_token(&ptr,name ,NULL, sizeof(pstring)))
412       ++count;
413     if (next_token(&ptr,flags,NULL, sizeof(flags)))
414       ++count;
415     if (next_token(&ptr,extra,NULL, sizeof(extra)))
416       ++count;
417
418     if (count <= 0)
419       continue;
420
421     if (count > 0 && count < 2)
422     {
423       DEBUG(0,("getlmhostsent: Ill formed hosts line [%s]\n",line));
424       continue;
425     }
426
427     if (count >= 4)
428     {
429       DEBUG(0,("getlmhostsent: too many columns in lmhosts file (obsolete syntax)\n"));
430       continue;
431     }
432
433     DEBUG(4, ("getlmhostsent: lmhost entry: %s %s %s\n", ip, name, flags));
434
435     if (strchr(flags,'G') || strchr(flags,'S'))
436     {
437       DEBUG(0,("getlmhostsent: group flag in lmhosts ignored (obsolete)\n"));
438       continue;
439     }
440
441     *ipaddr = *interpret_addr2(ip);
442
443     /* Extra feature. If the name ends in '#XX', where XX is a hex number,
444        then only add that name type. */
445     if((ptr = strchr(name, '#')) != NULL)
446     {
447       char *endptr;
448
449       ptr++;
450       *name_type = (int)strtol(ptr, &endptr,0);
451
452       if(!*ptr || (endptr == ptr))
453       {
454         DEBUG(0,("getlmhostsent: invalid name %s containing '#'.\n", name));
455         continue;
456       }
457
458       *(--ptr) = '\0'; /* Truncate at the '#' */
459     }
460
461     return True;
462   }
463
464   return False;
465 }
466
467 /********************************************************
468  Finish parsing the lmhosts file.
469 *********************************************************/
470
471 void endlmhosts(FILE *fp)
472 {
473   fclose(fp);
474 }
475
476
477
478 /********************************************************
479 resolve via "bcast" method
480 *********************************************************/
481 static BOOL resolve_bcast(const char *name, struct in_addr *return_ip, int name_type)
482 {
483         int sock, i;
484         
485         /*
486          * "bcast" means do a broadcast lookup on all the local interfaces.
487          */
488
489         DEBUG(3,("resolve_name: Attempting broadcast lookup for name %s<0x20>\n", name));
490
491         sock = open_socket_in( SOCK_DGRAM, 0, 3,
492                                interpret_addr(lp_socket_address()) );
493
494         if (sock != -1) {
495                 struct in_addr *iplist = NULL;
496                 int count;
497                 int num_interfaces = iface_count();
498                 set_socket_options(sock,"SO_BROADCAST");
499                 /*
500                  * Lookup the name on all the interfaces, return on
501                  * the first successful match.
502                  */
503                 for( i = 0; i < num_interfaces; i++) {
504                         struct in_addr sendto_ip;
505                         /* Done this way to fix compiler error on IRIX 5.x */
506                         sendto_ip = *iface_bcast(*iface_n_ip(i));
507                         iplist = name_query(sock, name, name_type, True, 
508                                             True, sendto_ip, &count, NULL);
509                         if(iplist != NULL) {
510                                 *return_ip = iplist[0];
511                                 free((char *)iplist);
512                                 close(sock);
513                                 return True;
514                         }
515                 }
516                 close(sock);
517         }
518
519         return False;
520 }
521
522
523
524 /********************************************************
525 resolve via "wins" method
526 *********************************************************/
527 static BOOL resolve_wins(const char *name, struct in_addr *return_ip, int name_type)
528 {
529       int sock;
530       struct in_addr wins_ip;
531       BOOL wins_ismyip;
532
533       /*
534        * "wins" means do a unicast lookup to the WINS server.
535        * Ignore if there is no WINS server specified or if the
536        * WINS server is one of our interfaces (if we're being
537        * called from within nmbd - we can't do this call as we
538        * would then block).
539        */
540
541       DEBUG(3,("resolve_name: Attempting wins lookup for name %s<0x20>\n", name));
542
543       if(!*lp_wins_server()) {
544               DEBUG(3,("resolve_name: WINS server resolution selected and no WINS server present.\n"));
545               return False;
546       }
547
548       wins_ip = *interpret_addr2(lp_wins_server());
549       wins_ismyip = ismyip(wins_ip);
550
551       if((wins_ismyip && !global_in_nmbd) || !wins_ismyip) {
552               sock = open_socket_in( SOCK_DGRAM, 0, 3,
553                                      interpret_addr(lp_socket_address()) );
554               
555               if (sock != -1) {
556                       struct in_addr *iplist = NULL;
557                       int count;
558                       iplist = name_query(sock, name, name_type, False, 
559                                           True, wins_ip, &count, NULL);
560                       if(iplist != NULL) {
561                               *return_ip = iplist[0];
562                               free((char *)iplist);
563                               close(sock);
564                               return True;
565                       }
566                       close(sock);
567               }
568       }
569
570       return False;
571 }
572
573
574 /********************************************************
575 resolve via "lmhosts" method
576 *********************************************************/
577 static BOOL resolve_lmhosts(const char *name, struct in_addr *return_ip, int name_type)
578 {
579         /*
580          * "lmhosts" means parse the local lmhosts file.
581          */
582         
583         FILE *fp;
584         pstring lmhost_name;
585         int name_type2;
586
587         DEBUG(3,("resolve_name: Attempting lmhosts lookup for name %s\n", name));
588
589         fp = startlmhosts( LMHOSTSFILE );
590         if(fp) {
591                 while (getlmhostsent(fp, lmhost_name, &name_type2, return_ip)) {
592                         if (strequal(name, lmhost_name) && 
593                             name_type == name_type2) {
594                                 endlmhosts(fp);
595                                 return True; 
596                         }
597                 }
598                 endlmhosts(fp);
599         }
600         return False;
601 }
602
603
604 /********************************************************
605 resolve via "hosts" method
606 *********************************************************/
607 static BOOL resolve_hosts(const char *name, struct in_addr *return_ip)
608 {
609         /*
610          * "host" means do a localhost, or dns lookup.
611          */
612         struct hostent *hp;
613
614         DEBUG(3,("resolve_name: Attempting host lookup for name %s\n", name));
615         
616         if (((hp = Get_Hostbyname(name)) != NULL) && (hp->h_addr != NULL)) {
617                 putip((char *)return_ip,(char *)hp->h_addr);
618                 return True;
619         }
620         return False;
621 }
622
623
624 /********************************************************
625  Resolve a name into an IP address. Use this function if
626  the string is either an IP address, DNS or host name
627  or NetBIOS name. This uses the name switch in the
628  smb.conf to determine the order of name resolution.
629 *********************************************************/
630 BOOL is_ip_address(const char *name)
631 {
632   int i;
633   for (i=0; name[i]; i++)
634     if (!(isdigit((int)name[i]) || name[i] == '.'))
635         return False;
636    
637   return True;
638 }
639
640 /********************************************************
641  Resolve a name into an IP address. Use this function if
642  the string is either an IP address, DNS or host name
643  or NetBIOS name. This uses the name switch in the
644  smb.conf to determine the order of name resolution.
645 *********************************************************/
646 BOOL resolve_name(const char *name, struct in_addr *return_ip, int name_type)
647 {
648   pstring name_resolve_list;
649   fstring tok;
650   char *ptr;
651
652   if (strcmp(name,"0.0.0.0") == 0) {
653     return_ip->s_addr = 0;
654     return True;
655   }
656   if (strcmp(name,"255.255.255.255") == 0) {
657     return_ip->s_addr = 0xFFFFFFFF;
658     return True;
659   }
660    
661   /* if it's in the form of an IP address then get the lib to interpret it */
662   if (is_ip_address(name)) {
663     return_ip->s_addr = inet_addr(name);
664     return True;
665   }
666
667   pstrcpy(name_resolve_list, lp_name_resolve_order());
668   ptr = name_resolve_list;
669   if (!ptr || !*ptr) ptr = "host";
670
671   while (next_token(&ptr, tok, LIST_SEP, sizeof(tok))) {
672           if((strequal(tok, "host") || strequal(tok, "hosts"))) {
673                   if (name_type == 0x20 && resolve_hosts(name, return_ip)) {
674                           return True;
675                   }
676           } else if(strequal( tok, "lmhosts")) {
677                   if (resolve_lmhosts(name, return_ip, name_type)) {
678                           return True;
679                   }
680           } else if(strequal( tok, "wins")) {
681                   /* don't resolve 1D via WINS */
682                   if (name_type != 0x1D &&
683                       resolve_wins(name, return_ip, name_type)) {
684                           return True;
685                   }
686           } else if(strequal( tok, "bcast")) {
687                   if (resolve_bcast(name, return_ip, name_type)) {
688                           return True;
689                   }
690           } else {
691                   DEBUG(0,("resolve_name: unknown name switch type %s\n", tok));
692           }
693   }
694
695   return False;
696 }
697
698 /********************************************************
699  resolve a name of format \\server_name or \\ipaddress
700  into a name.  also, cut the \\ from the front for us.
701 *********************************************************/
702 BOOL resolve_srv_name(const char* srv_name, fstring dest_host,
703                                 struct in_addr *ip)
704 {
705         BOOL ret;
706         const char *sv_name = srv_name;
707
708         DEBUG(10,("resolve_srv_name: %s\n", srv_name));
709
710         if (srv_name == NULL || strequal("\\\\.", srv_name))
711         {
712                 fstrcpy(dest_host, global_myname);
713                 ip = interpret_addr2("127.0.0.1");
714                 return True;
715         }
716
717         if (strnequal("\\\\", srv_name, 2))
718         {
719                 sv_name = &srv_name[2];
720         }
721
722         fstrcpy(dest_host, sv_name);
723         ret = resolve_name(dest_host, ip, 0x20);
724         
725         if (is_ip_address(dest_host))
726         {
727                 fstrcpy(dest_host, "*SMBSERVER");
728         }
729         
730         return ret;
731 }
732
733 /********************************************************
734 find the IP address of the master browser or DMB for a workgroup
735 *********************************************************/
736 BOOL find_master_ip(char *group, struct in_addr *master_ip)
737 {
738         if (resolve_name(group, master_ip, 0x1D)) return True;
739
740         return resolve_name(group, master_ip, 0x1B);
741 }