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