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