892752209029a22a98158f6e34be84c59ef45a3c
[kai/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       strcat(flags, (p[0] & 0x80) ? "<GROUP> " : "        ");
55       if ((p[0] & 0x60) == 0x00) strcat(flags,"B ");
56       if ((p[0] & 0x60) == 0x20) strcat(flags,"P ");
57       if ((p[0] & 0x60) == 0x40) strcat(flags,"M ");
58       if ((p[0] & 0x60) == 0x60) strcat(flags,"H ");
59       if (p[0] & 0x10) strcat(flags,"<DEREGISTERING> ");
60       if (p[0] & 0x08) strcat(flags,"<CONFLICT> ");
61       if (p[0] & 0x04) strcat(flags,"<ACTIVE> ");
62       if (p[0] & 0x02) strcat(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(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)())
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 = (time(NULL)%(unsigned)0x7FFF) + 
106     (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, 
198                            BOOL bcast,BOOL recurse,
199                            struct in_addr to_ip, int *count, void (*fn)())
200 {
201   BOOL found=False;
202   int i, retries = 3;
203   int retry_time = bcast?250:2000;
204   struct timeval tval;
205   struct packet_struct p;
206   struct packet_struct *p2;
207   struct nmb_packet *nmb = &p.packet.nmb;
208   static int name_trn_id = 0;
209   struct in_addr *ip_list = NULL;
210
211   bzero((char *)&p,sizeof(p));
212   (*count) = 0;
213
214   if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + 
215     (getpid()%(unsigned)100);
216   name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
217
218   nmb->header.name_trn_id = name_trn_id;
219   nmb->header.opcode = 0;
220   nmb->header.response = False;
221   nmb->header.nm_flags.bcast = bcast;
222   nmb->header.nm_flags.recursion_available = False;
223   nmb->header.nm_flags.recursion_desired = recurse;
224   nmb->header.nm_flags.trunc = False;
225   nmb->header.nm_flags.authoritative = False;
226   nmb->header.rcode = 0;
227   nmb->header.qdcount = 1;
228   nmb->header.ancount = 0;
229   nmb->header.nscount = 0;
230   nmb->header.arcount = 0;
231
232   make_nmb_name(&nmb->question.question_name,name,name_type,scope);
233
234   nmb->question.question_type = 0x20;
235   nmb->question.question_class = 0x1;
236
237   p.ip = to_ip;
238   p.port = NMB_PORT;
239   p.fd = fd;
240   p.timestamp = time(NULL);
241   p.packet_type = NMB_PACKET;
242
243   GetTimeOfDay(&tval);
244
245   if (!send_packet(&p)) 
246     return NULL;
247
248   retries--;
249
250   while (1)
251     {
252       struct timeval tval2;
253       GetTimeOfDay(&tval2);
254       if (TvalDiff(&tval,&tval2) > retry_time) {
255         if (!retries) break;
256         if (!found && !send_packet(&p))
257           return NULL;
258         GetTimeOfDay(&tval);
259         retries--;
260       }
261
262       if ((p2=receive_packet(fd,NMB_PACKET,90)))
263         {     
264           struct nmb_packet *nmb2 = &p2->packet.nmb;
265           debug_nmb_packet(p2);
266
267           if (nmb->header.name_trn_id != nmb2->header.name_trn_id ||
268               !nmb2->header.response) {
269             /* its not for us - maybe deal with it later 
270                (put it on the queue?) */
271             if (fn) 
272               fn(p2);
273             else
274               free_packet(p2);
275             continue;
276           }
277           
278           if (nmb2->header.opcode != 0 ||
279               nmb2->header.nm_flags.bcast ||
280               nmb2->header.rcode ||
281               !nmb2->header.ancount) {
282             /* XXXX what do we do with this? could be a redirect, but
283                we'll discard it for the moment */
284             free_packet(p2);
285             continue;
286           }
287
288           ip_list = (struct in_addr *)Realloc(ip_list, sizeof(ip_list[0]) * 
289                                               ((*count)+nmb2->answers->rdlength/6));
290           if (ip_list) {
291                   DEBUG(fn?3:2,("Got a positive name query response from %s ( ",
292                                 inet_ntoa(p2->ip)));
293                   for (i=0;i<nmb2->answers->rdlength/6;i++) {
294                           putip((char *)&ip_list[(*count)],&nmb2->answers->rdata[2+i*6]);
295                           DEBUG(fn?3:2,("%s ",inet_ntoa(ip_list[(*count)])));
296                           (*count)++;
297                   }
298                   DEBUG(fn?3:2,(")\n"));
299           }
300           found=True; retries=0;
301           free_packet(p2);
302           if (fn) break;
303         }
304     }
305
306   return ip_list;
307 }
308
309 /********************************************************
310  Start parsing the lmhosts file.
311 *********************************************************/
312
313 FILE *startlmhosts(char *fname)
314 {
315   FILE *fp = fopen(fname,"r");
316   if (!fp) {
317     DEBUG(2,("startlmhosts: Can't open lmhosts file %s. Error was %s\n",
318              fname, strerror(errno)));
319     return NULL;
320   }
321   return fp;
322 }
323
324 /********************************************************
325  Parse the next line in the lmhosts file.
326 *********************************************************/
327
328 BOOL getlmhostsent( FILE *fp, char *name, int *name_type, struct in_addr *ipaddr)
329 {
330   pstring line;
331
332   while(!feof(fp) && !ferror(fp)) {
333     pstring ip,flags,extra;
334     char *ptr;
335     int count = 0;
336
337     *name_type = -1;
338
339     if (!fgets_slash(line,sizeof(pstring),fp))
340       continue;
341
342     if (*line == '#')
343       continue;
344
345     strcpy(ip,"");
346     strcpy(name,"");
347     strcpy(flags,"");
348
349     ptr = line;
350
351     if (next_token(&ptr,ip   ,NULL))
352       ++count;
353     if (next_token(&ptr,name ,NULL))
354       ++count;
355     if (next_token(&ptr,flags,NULL))
356       ++count;
357     if (next_token(&ptr,extra,NULL))
358       ++count;
359
360     if (count <= 0)
361       continue;
362
363     if (count > 0 && count < 2)
364     {
365       DEBUG(0,("getlmhostsent: Ill formed hosts line [%s]\n",line));
366       continue;
367     }
368
369     if (count >= 4)
370     {
371       DEBUG(0,("getlmhostsent: too many columns in lmhosts file (obsolete syntax)\n"));
372       continue;
373     }
374
375     DEBUG(4, ("getlmhostsent: lmhost entry: %s %s %s\n", ip, name, flags));
376
377     if (strchr(flags,'G') || strchr(flags,'S'))
378     {
379       DEBUG(0,("getlmhostsent: group flag in lmhosts ignored (obsolete)\n"));
380       continue;
381     }
382
383     *ipaddr = *interpret_addr2(ip);
384
385     /* Extra feature. If the name ends in '#XX', where XX is a hex number,
386        then only add that name type. */
387     if((ptr = strchr(name, '#')) != NULL)
388     {
389       char *endptr;
390
391       ptr++;
392       *name_type = (int)strtol(ptr, &endptr,0);
393
394       if(!*ptr || (endptr == ptr))
395       {
396         DEBUG(0,("getlmhostsent: invalid name %s containing '#'.\n", name));
397         continue;
398       }
399
400       *(--ptr) = '\0'; /* Truncate at the '#' */
401     }
402
403     return True;
404   }
405
406   return False;
407 }
408
409 /********************************************************
410  Finish parsing the lmhosts file.
411 *********************************************************/
412
413 void endlmhosts(FILE *fp)
414 {
415   fclose(fp);
416 }
417
418 /********************************************************
419  Resolve a name into an IP address. Use this function if
420  the string is either an IP address, DNS or host name
421  or NetBIOS name. This uses the name switch in the
422  smb.conf to determine the order of name resolution.
423 *********************************************************/
424
425 BOOL resolve_name(char *name, struct in_addr *return_ip)
426 {
427   int i;
428   BOOL pure_address = True;
429   pstring name_resolve_list;
430   fstring tok;
431   char *ptr;
432
433   if (strcmp(name,"0.0.0.0") == 0) {
434     return_ip->s_addr = 0;
435     return True;
436   }
437   if (strcmp(name,"255.255.255.255") == 0) {
438     return_ip->s_addr = 0xFFFFFFFF;
439     return True;
440   }
441    
442   for (i=0; pure_address && name[i]; i++)
443     if (!(isdigit(name[i]) || name[i] == '.'))
444       pure_address = False;
445    
446   /* if it's in the form of an IP address then get the lib to interpret it */
447   if (pure_address) {
448     return_ip->s_addr = inet_addr(name);
449     return True;
450   }
451
452   pstrcpy(name_resolve_list, lp_name_resolve_order());
453   ptr = name_resolve_list;
454   if (!ptr || !*ptr) ptr = "host";
455
456   while (next_token(&ptr, tok, LIST_SEP)) {
457     if(strequal(tok, "host") || strequal(tok, "hosts")) {
458
459       /*
460        * "host" means do a localhost, or dns lookup.
461        */
462
463       struct hostent *hp;
464
465       DEBUG(3,("resolve_name: Attempting host lookup for name %s\n", name));
466
467       if (((hp = Get_Hostbyname(name)) != NULL) && (hp->h_addr != NULL)) {
468         putip((char *)return_ip,(char *)hp->h_addr);
469         return True;
470       }
471
472     } else if(strequal( tok, "lmhosts")) {
473
474       /*
475        * "lmhosts" means parse the local lmhosts file.
476        */
477
478       FILE *fp;
479       pstring lmhost_name;
480       int name_type;
481
482       DEBUG(3,("resolve_name: Attempting lmhosts lookup for name %s\n", name));
483
484       fp = startlmhosts( LMHOSTSFILE );
485       if(fp) {
486         while( getlmhostsent(fp, lmhost_name, &name_type, return_ip ) ) {
487           if( strequal(name, lmhost_name )) {
488             endlmhosts(fp);
489             return True; 
490           }
491         }
492         endlmhosts(fp);
493       }
494
495     } else if(strequal( tok, "wins")) {
496
497       int sock;
498
499       /*
500        * "wins" means do a unicast lookup to the WINS server.
501        * Ignore if there is no WINS server specified or if the
502        * WINS server is one of our interfaces (if we're being
503        * called from within nmbd - we can't do this call as we
504        * would then block).
505        */
506
507       DEBUG(3,("resolve_name: Attempting wins lookup for name %s<0x20>\n", name));
508
509       if(*lp_wins_server()) {
510         struct in_addr wins_ip = *interpret_addr2(lp_wins_server());
511         BOOL wins_ismyip = ismyip(wins_ip);
512
513         if((wins_ismyip && !global_in_nmbd) || !wins_ismyip) {
514           sock = open_socket_in( SOCK_DGRAM, 0, 3,
515                                interpret_addr(lp_socket_address()) );
516
517           if (sock != -1) {
518             struct in_addr *iplist = NULL;
519             int count;
520             iplist = name_query(sock, name, 0x20, False, True, wins_ip, &count, NULL);
521             if(iplist != NULL) {
522               *return_ip = iplist[0];
523               free((char *)iplist);
524               close(sock);
525               return True;
526             }
527             close(sock);
528           }
529         }
530       } else {
531         DEBUG(3,("resolve_name: WINS server resolution selected and no WINS server present.\n"));
532       }
533     } else if(strequal( tok, "bcast")) {
534
535       int sock;
536
537       /*
538        * "bcast" means do a broadcast lookup on all the local interfaces.
539        */
540
541       DEBUG(3,("resolve_name: Attempting broadcast lookup for name %s<0x20>\n", name));
542
543       sock = open_socket_in( SOCK_DGRAM, 0, 3,
544                              interpret_addr(lp_socket_address()) );
545
546       if (sock != -1) {
547         struct in_addr *iplist = NULL;
548         int count;
549         int num_interfaces = iface_count();
550         set_socket_options(sock,"SO_BROADCAST");
551         /*
552          * Lookup the name on all the interfaces, return on
553          * the first successful match.
554          */
555         for( i = 0; i < num_interfaces; i++) {
556           struct in_addr 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 }