a9da735f362cd9bffcf8c7a9534154350a31d826
[nivanova/samba-autobuild/.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  Resolve a name into an IP address. Use this function if
419  the string is either an IP address, DNS or host name
420  or NetBIOS name. This uses the name switch in the
421  smb.conf to determine the order of name resolution.
422 *********************************************************/
423 BOOL resolve_name(char *name, struct in_addr *return_ip)
424 {
425   int i;
426   BOOL pure_address = True;
427   pstring name_resolve_list;
428   fstring tok;
429   char *ptr;
430
431   if (strcmp(name,"0.0.0.0") == 0) {
432     return_ip->s_addr = 0;
433     return True;
434   }
435   if (strcmp(name,"255.255.255.255") == 0) {
436     return_ip->s_addr = 0xFFFFFFFF;
437     return True;
438   }
439    
440   for (i=0; pure_address && name[i]; i++)
441     if (!(isdigit((int)name[i]) || name[i] == '.'))
442       pure_address = False;
443    
444   /* if it's in the form of an IP address then get the lib to interpret it */
445   if (pure_address) {
446     return_ip->s_addr = inet_addr(name);
447     return True;
448   }
449
450   pstrcpy(name_resolve_list, lp_name_resolve_order());
451   ptr = name_resolve_list;
452   if (!ptr || !*ptr) ptr = "host";
453
454   while (next_token(&ptr, tok, LIST_SEP, sizeof(tok))) {
455     if(strequal(tok, "host") || strequal(tok, "hosts")) {
456
457       /*
458        * "host" means do a localhost, or dns lookup.
459        */
460
461       struct hostent *hp;
462
463       DEBUG(3,("resolve_name: Attempting host lookup for name %s\n", name));
464
465       if (((hp = Get_Hostbyname(name)) != NULL) && (hp->h_addr != NULL)) {
466         putip((char *)return_ip,(char *)hp->h_addr);
467         return True;
468       }
469
470     } else if(strequal( tok, "lmhosts")) {
471
472       /*
473        * "lmhosts" means parse the local lmhosts file.
474        */
475
476       FILE *fp;
477       pstring lmhost_name;
478       int name_type;
479
480       DEBUG(3,("resolve_name: Attempting lmhosts lookup for name %s\n", name));
481
482       fp = startlmhosts( LMHOSTSFILE );
483       if(fp) {
484         while( getlmhostsent(fp, lmhost_name, &name_type, return_ip ) ) {
485           if( strequal(name, lmhost_name )) {
486             endlmhosts(fp);
487             return True; 
488           }
489         }
490         endlmhosts(fp);
491       }
492
493     } else if(strequal( tok, "wins")) {
494
495       int sock;
496
497       /*
498        * "wins" means do a unicast lookup to the WINS server.
499        * Ignore if there is no WINS server specified or if the
500        * WINS server is one of our interfaces (if we're being
501        * called from within nmbd - we can't do this call as we
502        * would then block).
503        */
504
505       DEBUG(3,("resolve_name: Attempting wins lookup for name %s<0x20>\n", name));
506
507       if(*lp_wins_server()) {
508         struct in_addr wins_ip = *interpret_addr2(lp_wins_server());
509         BOOL wins_ismyip = ismyip(wins_ip);
510
511         if((wins_ismyip && !global_in_nmbd) || !wins_ismyip) {
512           sock = open_socket_in( SOCK_DGRAM, 0, 3,
513                                interpret_addr(lp_socket_address()) );
514
515           if (sock != -1) {
516             struct in_addr *iplist = NULL;
517             int count;
518             iplist = name_query(sock, name, 0x20, False, True, wins_ip, &count, NULL);
519             if(iplist != NULL) {
520               *return_ip = iplist[0];
521               free((char *)iplist);
522               close(sock);
523               return True;
524             }
525             close(sock);
526           }
527         }
528       } else {
529         DEBUG(3,("resolve_name: WINS server resolution selected and no WINS server present.\n"));
530       }
531     } else if(strequal( tok, "bcast")) {
532
533       int sock;
534
535       /*
536        * "bcast" means do a broadcast lookup on all the local interfaces.
537        */
538
539       DEBUG(3,("resolve_name: Attempting broadcast lookup for name %s<0x20>\n", name));
540
541       sock = open_socket_in( SOCK_DGRAM, 0, 3,
542                              interpret_addr(lp_socket_address()) );
543
544       if (sock != -1) {
545         struct in_addr *iplist = NULL;
546         int count;
547         int num_interfaces = iface_count();
548         set_socket_options(sock,"SO_BROADCAST");
549         /*
550          * Lookup the name on all the interfaces, return on
551          * the first successful match.
552          */
553         for( i = 0; i < num_interfaces; i++) {
554           struct in_addr sendto_ip;
555           /* Done this way to fix compiler error on IRIX 5.x */
556           sendto_ip = *iface_bcast(*iface_n_ip(i));
557           iplist = name_query(sock, name, 0x20, True, False, sendto_ip, &count, NULL);
558           if(iplist != NULL) {
559             *return_ip = iplist[0];
560             free((char *)iplist);
561             close(sock);
562             return True;
563           }
564         }
565         close(sock);
566       }
567
568     } else {
569       DEBUG(0,("resolve_name: unknown name switch type %s\n", tok));
570     }
571   }
572
573   return False;
574 }
575
576
577
578 /********************************************************
579 find the IP address of the master browser for a workgroup
580 *********************************************************/
581 BOOL find_master(char *group, struct in_addr *master_ip)
582 {
583         int sock;
584         struct in_addr *iplist = NULL;
585         int count, i;
586         int num_interfaces = iface_count();
587
588         sock = open_socket_in( SOCK_DGRAM, 0, 3,
589                                interpret_addr(lp_socket_address()) );
590
591         if (sock == -1) return False;
592
593         set_socket_options(sock,"SO_BROADCAST");
594
595         /*
596          * Lookup the name on all the interfaces, return on
597          * the first successful match.
598          */
599         for( i = 0; i < num_interfaces; i++) {
600                 struct in_addr sendto_ip;
601                 /* Done this way to fix compiler error on IRIX 5.x */
602                 sendto_ip = *iface_bcast(*iface_n_ip(i));
603                 iplist = name_query(sock, group, 0x1D, True, False, 
604                                     sendto_ip, &count, NULL);
605                 if(iplist != NULL) {
606                         *master_ip = iplist[0];
607                         free((char *)iplist);
608                         close(sock);
609                         return True;
610                 }
611         }
612         close(sock);
613         return False;
614 }