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