docs: Remove comment about default backend.
[kai/samba.git] / source3 / nsswitch / wins.c
1 /* 
2    Unix SMB/CIFS implementation.
3    a WINS nsswitch module 
4    Copyright (C) Andrew Tridgell 1999
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18    
19 */
20
21 #include "includes.h"
22 #ifdef HAVE_NS_API_H
23
24 #include <ns_daemon.h>
25 #endif
26
27 #if HAVE_PTHREAD_H
28 #include <pthread.h>
29 #endif
30
31 #if HAVE_PTHREAD
32 static pthread_mutex_t wins_nss_mutex = PTHREAD_MUTEX_INITIALIZER;
33 #endif
34
35 #ifndef INADDRSZ
36 #define INADDRSZ 4
37 #endif
38
39 static int initialised;
40
41 extern bool AllowDebugChange;
42
43 NSS_STATUS _nss_wins_gethostbyname_r(const char *hostname, struct hostent *he,
44                           char *buffer, size_t buflen, int *h_errnop);
45 NSS_STATUS _nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
46                            char *buffer, size_t buflen, int *h_errnop);
47
48 /* Use our own create socket code so we don't recurse.... */
49
50 static int wins_lookup_open_socket_in(void)
51 {
52         struct sockaddr_in sock;
53         int val=1;
54         int res;
55
56         memset((char *)&sock,'\0',sizeof(sock));
57
58 #ifdef HAVE_SOCK_SIN_LEN
59         sock.sin_len = sizeof(sock);
60 #endif
61         sock.sin_port = 0;
62         sock.sin_family = AF_INET;
63         sock.sin_addr.s_addr = interpret_addr("0.0.0.0");
64         res = socket(AF_INET, SOCK_DGRAM, 0);
65         if (res == -1)
66                 return -1;
67
68         if (setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)) != 0) {
69                 close(res);
70                 return -1;
71         }
72 #ifdef SO_REUSEPORT
73         if (setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)) != 0) {
74                 close(res);
75                 return -1;
76         }
77 #endif /* SO_REUSEPORT */
78
79         /* now we've got a socket - we need to bind it */
80
81         if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) {
82                 close(res);
83                 return(-1);
84         }
85
86         set_socket_options(res,"SO_BROADCAST");
87
88         return res;
89 }
90
91
92 static void nss_wins_init(void)
93 {
94         initialised = 1;
95         DEBUGLEVEL = 0;
96         AllowDebugChange = False;
97
98         TimeInit();
99         setup_logging("nss_wins",False);
100         load_case_tables();
101         lp_load(get_dyn_CONFIGFILE(),True,False,False,True);
102         load_interfaces();
103 }
104
105 static struct in_addr *lookup_byname_backend(const char *name, int *count)
106 {
107         int fd = -1;
108         struct ip_service *address = NULL;
109         struct in_addr *ret = NULL;
110         int j, flags = 0;
111
112         if (!initialised) {
113                 nss_wins_init();
114         }
115
116         *count = 0;
117
118         /* always try with wins first */
119         if (NT_STATUS_IS_OK(resolve_wins(name,0x00,&address,count))) {
120                 if ( (ret = SMB_MALLOC_P(struct in_addr)) == NULL ) {
121                         free( address );
122                         return NULL;
123                 }
124                 if (address[0].ss.ss_family != AF_INET) {
125                         free(address);
126                         free(ret);
127                         return NULL;
128                 }
129                 *ret = ((struct sockaddr_in *)&address[0].ss)->sin_addr;
130                 free( address );
131                 return ret;
132         }
133
134         fd = wins_lookup_open_socket_in();
135         if (fd == -1) {
136                 return NULL;
137         }
138
139         /* uggh, we have to broadcast to each interface in turn */
140         for (j=iface_count() - 1;j >= 0;j--) {
141                 const struct in_addr *bcast = iface_n_bcast_v4(j);
142                 struct sockaddr_storage ss;
143                 struct sockaddr_storage *pss;
144                 if (!bcast) {
145                         continue;
146                 }
147                 in_addr_to_sockaddr_storage(&ss, *bcast);
148                 pss = name_query(fd,name,0x00,True,True,&ss,count, &flags, NULL);
149                 if (pss) {
150                         if ((ret = SMB_MALLOC_P(struct in_addr)) == NULL) {
151                                 return NULL;
152                         }
153                         *ret = ((struct sockaddr_in *)pss)->sin_addr;
154                         break;
155                 }
156         }
157
158         close(fd);
159         return ret;
160 }
161
162 #ifdef HAVE_NS_API_H
163
164 static NODE_STATUS_STRUCT *lookup_byaddr_backend(char *addr, int *count)
165 {
166         int fd;
167         struct sockaddr_storage ss;
168         struct nmb_name nname;
169         NODE_STATUS_STRUCT *status;
170
171         if (!initialised) {
172                 nss_wins_init();
173         }
174
175         fd = wins_lookup_open_socket_in();
176         if (fd == -1)
177                 return NULL;
178
179         make_nmb_name(&nname, "*", 0);
180         if (!interpret_string_addr(&ss, addr, AI_NUMERICHOST)) {
181                 return NULL;
182         }
183         status = node_status_query(fd, &nname, &ss, count, NULL);
184
185         close(fd);
186         return status;
187 }
188
189 /* IRIX version */
190
191 int init(void)
192 {
193         nsd_logprintf(NSD_LOG_MIN, "entering init (wins)\n");
194         nss_wins_init();
195         return NSD_OK;
196 }
197
198 int lookup(nsd_file_t *rq)
199 {
200         char *map;
201         char *key;
202         char *addr;
203         struct in_addr *ip_list;
204         NODE_STATUS_STRUCT *status;
205         int i, count, len, size;
206         char response[1024];
207         bool found = False;
208
209         nsd_logprintf(NSD_LOG_MIN, "entering lookup (wins)\n");
210         if (! rq) 
211                 return NSD_ERROR;
212
213         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
214         if (! map) {
215                 rq->f_status = NS_FATAL;
216                 return NSD_ERROR;
217         }
218
219         key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
220         if (! key || ! *key) {
221                 rq->f_status = NS_FATAL;
222                 return NSD_ERROR;
223         }
224
225         response[0] = '\0';
226         len = sizeof(response) - 2;
227
228         /* 
229          * response needs to be a string of the following format
230          * ip_address[ ip_address]*\tname[ alias]*
231          */
232         if (StrCaseCmp(map,"hosts.byaddr") == 0) {
233                 if ( status = lookup_byaddr_backend(key, &count)) {
234                     size = strlen(key) + 1;
235                     if (size > len) {
236                         free(status);
237                         return NSD_ERROR;
238                     }
239                     len -= size;
240                     strncat(response,key,size);
241                     strncat(response,"\t",1);
242                     for (i = 0; i < count; i++) {
243                         /* ignore group names */
244                         if (status[i].flags & 0x80) continue;
245                         if (status[i].type == 0x20) {
246                                 size = sizeof(status[i].name) + 1;
247                                 if (size > len) {
248                                     free(status);
249                                     return NSD_ERROR;
250                                 }
251                                 len -= size;
252                                 strncat(response, status[i].name, size);
253                                 strncat(response, " ", 1);
254                                 found = True;
255                         }
256                     }
257                     response[strlen(response)-1] = '\n';
258                     free(status);
259                 }
260         } else if (StrCaseCmp(map,"hosts.byname") == 0) {
261             if (ip_list = lookup_byname_backend(key, &count)) {
262                 for (i = count; i ; i--) {
263                     addr = inet_ntoa(ip_list[i-1]);
264                     size = strlen(addr) + 1;
265                     if (size > len) {
266                         free(ip_list);
267                         return NSD_ERROR;
268                     }
269                     len -= size;
270                     if (i != 0)
271                         response[strlen(response)-1] = ' ';
272                     strncat(response,addr,size);
273                     strncat(response,"\t",1);
274                 }
275                 size = strlen(key) + 1;
276                 if (size > len) {
277                     free(ip_list);
278                     return NSD_ERROR;
279                 }   
280                 strncat(response,key,size);
281                 strncat(response,"\n",1);
282                 found = True;
283                 free(ip_list);
284             }
285         }
286
287         if (found) {
288             nsd_logprintf(NSD_LOG_LOW, "lookup (wins %s) %s\n",map,response);
289             nsd_set_result(rq,NS_SUCCESS,response,strlen(response),VOLATILE);
290             return NSD_OK;
291         }
292         nsd_logprintf(NSD_LOG_LOW, "lookup (wins) not found\n");
293         rq->f_status = NS_NOTFOUND;
294         return NSD_NEXT;
295 }
296
297 #else
298
299 /* Allocate some space from the nss static buffer.  The buffer and buflen
300    are the pointers passed in by the C library to the _nss_*_*
301    functions. */
302
303 static char *get_static(char **buffer, size_t *buflen, int len)
304 {
305         char *result;
306
307         /* Error check.  We return false if things aren't set up right, or
308            there isn't enough buffer space left. */
309         
310         if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
311                 return NULL;
312         }
313
314         /* Return an index into the static buffer */
315
316         result = *buffer;
317         *buffer += len;
318         *buflen -= len;
319
320         return result;
321 }
322
323 /****************************************************************************
324 gethostbyname() - we ignore any domain portion of the name and only
325 handle names that are at most 15 characters long
326   **************************************************************************/
327 NSS_STATUS
328 _nss_wins_gethostbyname_r(const char *hostname, struct hostent *he,
329                           char *buffer, size_t buflen, int *h_errnop)
330 {
331         NSS_STATUS nss_status = NSS_STATUS_SUCCESS;
332         struct in_addr *ip_list;
333         int i, count;
334         fstring name;
335         size_t namelen;
336                 
337 #if HAVE_PTHREAD
338         pthread_mutex_lock(&wins_nss_mutex);
339 #endif
340
341         memset(he, '\0', sizeof(*he));
342         fstrcpy(name, hostname);
343
344         /* Do lookup */
345
346         ip_list = lookup_byname_backend(name, &count);
347
348         if (!ip_list) {
349                 nss_status = NSS_STATUS_NOTFOUND;
350                 goto out;
351         }
352
353         /* Copy h_name */
354
355         namelen = strlen(name) + 1;
356
357         if ((he->h_name = get_static(&buffer, &buflen, namelen)) == NULL) {
358                 free(ip_list);
359                 nss_status = NSS_STATUS_TRYAGAIN;
360                 goto out;
361         }
362
363         memcpy(he->h_name, name, namelen);
364
365         /* Copy h_addr_list, align to pointer boundary first */
366
367         if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
368                 i = sizeof(char*) - i;
369
370         if (get_static(&buffer, &buflen, i) == NULL) {
371                 free(ip_list);
372                 nss_status = NSS_STATUS_TRYAGAIN;
373                 goto out;
374         }
375
376         if ((he->h_addr_list = (char **)get_static(
377                      &buffer, &buflen, (count + 1) * sizeof(char *))) == NULL) {
378                 free(ip_list);
379                 nss_status = NSS_STATUS_TRYAGAIN;
380                 goto out;
381         }
382
383         for (i = 0; i < count; i++) {
384                 if ((he->h_addr_list[i] = get_static(&buffer, &buflen,
385                                                      INADDRSZ)) == NULL) {
386                         free(ip_list);
387                         nss_status = NSS_STATUS_TRYAGAIN;
388                         goto out;
389                 }
390                 memcpy(he->h_addr_list[i], &ip_list[i], INADDRSZ);
391         }
392
393         he->h_addr_list[count] = NULL;
394
395         free(ip_list);
396
397         /* Set h_addr_type and h_length */
398
399         he->h_addrtype = AF_INET;
400         he->h_length = INADDRSZ;
401
402         /* Set h_aliases */
403
404         if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
405                 i = sizeof(char*) - i;
406
407         if (get_static(&buffer, &buflen, i) == NULL) {
408                 nss_status = NSS_STATUS_TRYAGAIN;
409                 goto out;
410         }
411
412         if ((he->h_aliases = (char **)get_static(
413                      &buffer, &buflen, sizeof(char *))) == NULL) {
414                 nss_status = NSS_STATUS_TRYAGAIN;
415                 goto out;
416         }
417
418         he->h_aliases[0] = NULL;
419
420         nss_status = NSS_STATUS_SUCCESS;
421
422   out:
423
424 #if HAVE_PTHREAD
425         pthread_mutex_unlock(&wins_nss_mutex);
426 #endif
427         return nss_status;
428 }
429
430
431 NSS_STATUS
432 _nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
433                            char *buffer, size_t buflen, int *h_errnop)
434 {
435         NSS_STATUS nss_status;
436
437         if(af!=AF_INET) {
438                 *h_errnop = NO_DATA;
439                 nss_status = NSS_STATUS_UNAVAIL;
440         } else {
441                 nss_status = _nss_wins_gethostbyname_r(
442                                 name, he, buffer, buflen, h_errnop);
443         }
444         return nss_status;
445 }
446 #endif