chgpasswd.c, ipc.c, loadparm.c: Added boolean "unix password sync"
[sfrench/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       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   while (next_token(&ptr, tok, LIST_SEP)) {
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 = *iface_bcast(*iface_n_ip(i));
555           iplist = name_query(sock, name, 0x20, True, False, sendto_ip, &count, NULL);
556           if(iplist != NULL) {
557             *return_ip = iplist[0];
558             free((char *)iplist);
559             close(sock);
560             return True;
561           }
562         }
563         close(sock);
564       }
565
566     } else {
567       DEBUG(0,("resolve_name: unknown name switch type %s\n", tok));
568     }
569   }
570
571   return False;
572 }