f5fd7a775e773e5374665bb1160ee4e585ec10f8
[samba.git] / 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 #include "nsswitch/winbind_nss.h"
23
24 #ifdef HAVE_NS_API_H
25
26 #include <ns_daemon.h>
27 #endif
28
29 #if HAVE_PTHREAD_H
30 #include <pthread.h>
31 #endif
32
33 #if HAVE_PTHREAD
34 static pthread_mutex_t wins_nss_mutex = PTHREAD_MUTEX_INITIALIZER;
35 #endif
36
37 #ifndef INADDRSZ
38 #define INADDRSZ 4
39 #endif
40
41 static int initialised;
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 static void nss_wins_init(void)
49 {
50         initialised = 1;
51         load_case_tables_library();
52         lp_set_cmdline("log level", "0");
53
54         TimeInit();
55         setup_logging("nss_wins",False);
56         lp_load(get_dyn_CONFIGFILE(),True,False,False,True);
57         load_interfaces();
58 }
59
60 static struct in_addr *lookup_byname_backend(const char *name, int *count)
61 {
62         struct ip_service *address = NULL;
63         struct in_addr *ret = NULL;
64         int j;
65
66         if (!initialised) {
67                 nss_wins_init();
68         }
69
70         *count = 0;
71
72         /* always try with wins first */
73         if (NT_STATUS_IS_OK(resolve_wins(name,0x00,&address,count))) {
74                 if ( (ret = SMB_MALLOC_P(struct in_addr)) == NULL ) {
75                         free( address );
76                         return NULL;
77                 }
78                 if (address[0].ss.ss_family != AF_INET) {
79                         free(address);
80                         free(ret);
81                         return NULL;
82                 }
83                 *ret = ((struct sockaddr_in *)(void *)&address[0].ss)
84                         ->sin_addr;
85                 free( address );
86                 return ret;
87         }
88
89         /* uggh, we have to broadcast to each interface in turn */
90         for (j=iface_count() - 1;j >= 0;j--) {
91                 const struct in_addr *bcast = iface_n_bcast_v4(j);
92                 struct sockaddr_storage ss;
93                 struct sockaddr_storage *pss;
94                 NTSTATUS status;
95
96                 if (!bcast) {
97                         continue;
98                 }
99                 in_addr_to_sockaddr_storage(&ss, *bcast);
100                 status = name_query(name, 0x00, True, True, &ss,
101                                     NULL, &pss, count, NULL);
102                 if (NT_STATUS_IS_OK(status) && (*count > 0)) {
103                         if ((ret = SMB_MALLOC_P(struct in_addr)) == NULL) {
104                                 return NULL;
105                         }
106                         *ret = ((struct sockaddr_in *)pss)->sin_addr;
107                         TALLOC_FREE(pss);
108                         break;
109                 }
110         }
111
112         return ret;
113 }
114
115 #ifdef HAVE_NS_API_H
116
117 static struct node_status *lookup_byaddr_backend(char *addr, int *count)
118 {
119         struct sockaddr_storage ss;
120         struct nmb_name nname;
121         struct node_status *result;
122         NTSTATUS status;
123
124         if (!initialised) {
125                 nss_wins_init();
126         }
127
128         make_nmb_name(&nname, "*", 0);
129         if (!interpret_string_addr(&ss, addr, AI_NUMERICHOST)) {
130                 return NULL;
131         }
132         status = node_status_query(NULL, &nname, &ss, &result, count, NULL);
133         if (!NT_STATUS_IS_OK(status)) {
134                 return NULL;
135         }
136
137         return result;
138 }
139
140 /* IRIX version */
141
142 int init(void)
143 {
144         nsd_logprintf(NSD_LOG_MIN, "entering init (wins)\n");
145         nss_wins_init();
146         return NSD_OK;
147 }
148
149 int lookup(nsd_file_t *rq)
150 {
151         char *map;
152         char *key;
153         char *addr;
154         struct in_addr *ip_list;
155         struct node_status *status;
156         int i, count, len, size;
157         char response[1024];
158         bool found = False;
159
160         nsd_logprintf(NSD_LOG_MIN, "entering lookup (wins)\n");
161         if (! rq)
162                 return NSD_ERROR;
163
164         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
165         if (! map) {
166                 rq->f_status = NS_FATAL;
167                 return NSD_ERROR;
168         }
169
170         key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
171         if (! key || ! *key) {
172                 rq->f_status = NS_FATAL;
173                 return NSD_ERROR;
174         }
175
176         response[0] = '\0';
177         len = sizeof(response) - 2;
178
179         /*
180          * response needs to be a string of the following format
181          * ip_address[ ip_address]*\tname[ alias]*
182          */
183         if (StrCaseCmp(map,"hosts.byaddr") == 0) {
184                 if ( status = lookup_byaddr_backend(key, &count)) {
185                     size = strlen(key) + 1;
186                     if (size > len) {
187                         talloc_free(status);
188                         return NSD_ERROR;
189                     }
190                     len -= size;
191                     strncat(response,key,size);
192                     strncat(response,"\t",1);
193                     for (i = 0; i < count; i++) {
194                         /* ignore group names */
195                         if (status[i].flags & 0x80) continue;
196                         if (status[i].type == 0x20) {
197                                 size = sizeof(status[i].name) + 1;
198                                 if (size > len) {
199                                     talloc_free(status);
200                                     return NSD_ERROR;
201                                 }
202                                 len -= size;
203                                 strncat(response, status[i].name, size);
204                                 strncat(response, " ", 1);
205                                 found = True;
206                         }
207                     }
208                     response[strlen(response)-1] = '\n';
209                     talloc_free(status);
210                 }
211         } else if (StrCaseCmp(map,"hosts.byname") == 0) {
212             if (ip_list = lookup_byname_backend(key, &count)) {
213                 for (i = count; i ; i--) {
214                     addr = inet_ntoa(ip_list[i-1]);
215                     size = strlen(addr) + 1;
216                     if (size > len) {
217                         free(ip_list);
218                         return NSD_ERROR;
219                     }
220                     len -= size;
221                     if (i != 0)
222                         response[strlen(response)-1] = ' ';
223                     strncat(response,addr,size);
224                     strncat(response,"\t",1);
225                 }
226                 size = strlen(key) + 1;
227                 if (size > len) {
228                     free(ip_list);
229                     return NSD_ERROR;
230                 }
231                 strncat(response,key,size);
232                 strncat(response,"\n",1);
233                 found = True;
234                 free(ip_list);
235             }
236         }
237
238         if (found) {
239             nsd_logprintf(NSD_LOG_LOW, "lookup (wins %s) %s\n",map,response);
240             nsd_set_result(rq,NS_SUCCESS,response,strlen(response),VOLATILE);
241             return NSD_OK;
242         }
243         nsd_logprintf(NSD_LOG_LOW, "lookup (wins) not found\n");
244         rq->f_status = NS_NOTFOUND;
245         return NSD_NEXT;
246 }
247
248 #else
249
250 /* Allocate some space from the nss static buffer.  The buffer and buflen
251    are the pointers passed in by the C library to the _nss_*_*
252    functions. */
253
254 static char *get_static(char **buffer, size_t *buflen, int len)
255 {
256         char *result;
257
258         /* Error check.  We return false if things aren't set up right, or
259            there isn't enough buffer space left. */
260
261         if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
262                 return NULL;
263         }
264
265         /* Return an index into the static buffer */
266
267         result = *buffer;
268         *buffer += len;
269         *buflen -= len;
270
271         return result;
272 }
273
274 /****************************************************************************
275 gethostbyname() - we ignore any domain portion of the name and only
276 handle names that are at most 15 characters long
277   **************************************************************************/
278 NSS_STATUS
279 _nss_wins_gethostbyname_r(const char *hostname, struct hostent *he,
280                           char *buffer, size_t buflen, int *h_errnop)
281 {
282         NSS_STATUS nss_status = NSS_STATUS_SUCCESS;
283         struct in_addr *ip_list;
284         int i, count;
285         fstring name;
286         size_t namelen;
287         TALLOC_CTX *frame;
288
289 #if HAVE_PTHREAD
290         pthread_mutex_lock(&wins_nss_mutex);
291 #endif
292
293         frame = talloc_stackframe();
294
295         memset(he, '\0', sizeof(*he));
296         fstrcpy(name, hostname);
297
298         /* Do lookup */
299
300         ip_list = lookup_byname_backend(name, &count);
301
302         if (!ip_list) {
303                 nss_status = NSS_STATUS_NOTFOUND;
304                 goto out;
305         }
306
307         /* Copy h_name */
308
309         namelen = strlen(name) + 1;
310
311         if ((he->h_name = get_static(&buffer, &buflen, namelen)) == NULL) {
312                 free(ip_list);
313                 nss_status = NSS_STATUS_TRYAGAIN;
314                 goto out;
315         }
316
317         memcpy(he->h_name, name, namelen);
318
319         /* Copy h_addr_list, align to pointer boundary first */
320
321         if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
322                 i = sizeof(char*) - i;
323
324         if (get_static(&buffer, &buflen, i) == NULL) {
325                 free(ip_list);
326                 nss_status = NSS_STATUS_TRYAGAIN;
327                 goto out;
328         }
329
330         if ((he->h_addr_list = (char **)get_static(
331                      &buffer, &buflen, (count + 1) * sizeof(char *))) == NULL) {
332                 free(ip_list);
333                 nss_status = NSS_STATUS_TRYAGAIN;
334                 goto out;
335         }
336
337         for (i = 0; i < count; i++) {
338                 if ((he->h_addr_list[i] = get_static(&buffer, &buflen,
339                                                      INADDRSZ)) == NULL) {
340                         free(ip_list);
341                         nss_status = NSS_STATUS_TRYAGAIN;
342                         goto out;
343                 }
344                 memcpy(he->h_addr_list[i], &ip_list[i], INADDRSZ);
345         }
346
347         he->h_addr_list[count] = NULL;
348
349         free(ip_list);
350
351         /* Set h_addr_type and h_length */
352
353         he->h_addrtype = AF_INET;
354         he->h_length = INADDRSZ;
355
356         /* Set h_aliases */
357
358         if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
359                 i = sizeof(char*) - i;
360
361         if (get_static(&buffer, &buflen, i) == NULL) {
362                 nss_status = NSS_STATUS_TRYAGAIN;
363                 goto out;
364         }
365
366         if ((he->h_aliases = (char **)get_static(
367                      &buffer, &buflen, sizeof(char *))) == NULL) {
368                 nss_status = NSS_STATUS_TRYAGAIN;
369                 goto out;
370         }
371
372         he->h_aliases[0] = NULL;
373
374         nss_status = NSS_STATUS_SUCCESS;
375
376   out:
377
378         TALLOC_FREE(frame);
379
380 #if HAVE_PTHREAD
381         pthread_mutex_unlock(&wins_nss_mutex);
382 #endif
383         return nss_status;
384 }
385
386
387 NSS_STATUS
388 _nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
389                            char *buffer, size_t buflen, int *h_errnop)
390 {
391         NSS_STATUS nss_status;
392
393         if(af!=AF_INET) {
394                 *h_errnop = NO_DATA;
395                 nss_status = NSS_STATUS_UNAVAIL;
396         } else {
397                 nss_status = _nss_wins_gethostbyname_r(
398                                 name, he, buffer, buflen, h_errnop);
399         }
400         return nss_status;
401 }
402 #endif