bootparamd.c revision 1.40 1 /* $NetBSD: bootparamd.c,v 1.40 2002/03/22 18:10:26 thorpej Exp $ */
2
3 /*
4 * This code is not copyright, and is placed in the public domain.
5 * Feel free to use and modify. Please send modifications and/or
6 * suggestions + bug fixes to Klas Heggemann <klas (at) nada.kth.se>
7 *
8 * Various small changes by Theo de Raadt <deraadt (at) fsa.ca>
9 * Parser rewritten (adding YP support) by Roland McGrath <roland (at) frob.com>
10 */
11
12 #include <sys/cdefs.h>
13 #ifndef lint
14 __RCSID("$NetBSD: bootparamd.c,v 1.40 2002/03/22 18:10:26 thorpej Exp $");
15 #endif
16
17 #include <sys/types.h>
18 #include <sys/ioctl.h>
19 #include <sys/stat.h>
20 #include <sys/socket.h>
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <err.h>
26 #include <fnmatch.h>
27 #include <netdb.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <syslog.h>
32 #include <unistd.h>
33 #include <util.h>
34 #include <ifaddrs.h>
35
36 #include <net/if.h>
37
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40
41 #include <rpc/rpc.h>
42 #include <rpc/pmap_clnt.h>
43 #include <rpcsvc/bootparam_prot.h>
44 #ifdef YP
45 #include <rpcsvc/ypclnt.h>
46 #endif
47
48 #include "pathnames.h"
49
50 #define MAXLEN 800
51
52 static char hostname[MAX_MACHINE_NAME];
53 static char askname[MAX_MACHINE_NAME];
54 static char domain_name[MAX_MACHINE_NAME];
55
56 extern void bootparamprog_1 __P((struct svc_req *, SVCXPRT *));
57
58 int _rpcsvcdirty = 0;
59 int _rpcpmstart = 0;
60 int debug = 0;
61 int dolog = 0;
62 struct in_addr route_addr;
63 struct sockaddr_in my_addr;
64 char *bootpfile = _PATH_BOOTPARAMS;
65
66 int main __P((int, char *[]));
67 int lookup_bootparam __P((char *, char *, char *, char **, char **));
68 void usage __P((void));
69 static int get_localaddr __P((const char *, struct sockaddr_in *));
70
71
72 /*
73 * ever familiar
74 */
75 int
76 main(argc, argv)
77 int argc;
78 char *argv[];
79 {
80 SVCXPRT *transp;
81 struct hostent *he;
82 struct stat buf;
83 int c;
84
85 while ((c = getopt(argc, argv, "dsr:f:")) != -1)
86 switch (c) {
87 case 'd':
88 debug = 1;
89 break;
90 case 'r':
91 if (isdigit(*optarg)) {
92 if (inet_aton(optarg, &route_addr) != 0)
93 break;
94 }
95 he = gethostbyname(optarg);
96 if (he == 0) {
97 warnx("no such host: %s", optarg);
98 usage();
99 }
100 memmove(&route_addr.s_addr, he->h_addr, he->h_length);
101 break;
102 case 'f':
103 bootpfile = optarg;
104 break;
105 case 's':
106 dolog = 1;
107 #ifndef LOG_DAEMON
108 openlog("rpc.bootparamd", 0, 0);
109 #else
110 openlog("rpc.bootparamd", 0, LOG_DAEMON);
111 setlogmask(LOG_UPTO(LOG_NOTICE));
112 #endif
113 break;
114 default:
115 usage();
116 }
117
118 if (stat(bootpfile, &buf))
119 err(1, "%s", bootpfile);
120
121 if (route_addr.s_addr == 0) {
122 if (get_localaddr(NULL, &my_addr) != 0)
123 errx(1, "router address not found");
124 route_addr.s_addr = my_addr.sin_addr.s_addr;
125 }
126 if (!debug) {
127 if (daemon(0, 0))
128 err(1, "can't detach from terminal");
129 }
130 pidfile(NULL);
131
132 (void) pmap_unset(BOOTPARAMPROG, BOOTPARAMVERS);
133
134 transp = svcudp_create(RPC_ANYSOCK);
135 if (transp == NULL)
136 errx(1, "can't create udp service");
137
138 if (!svc_register(transp, BOOTPARAMPROG, BOOTPARAMVERS, bootparamprog_1,
139 IPPROTO_UDP))
140 /*
141 * Do NOT change the "%u" in the format string below to "%lu". If your
142 * build fails update the "rpcgen" program and use "make cleandir" and
143 * "make includes" in "src/lib/librpcsvc" afterwards.
144 */
145 errx(1, "unable to register BOOTPARAMPROG version %u, udp",
146 BOOTPARAMVERS);
147
148 svc_run();
149 errx(1, "svc_run returned");
150 }
151
152 bp_whoami_res *
153 bootparamproc_whoami_1_svc(whoami, rqstp)
154 bp_whoami_arg *whoami;
155 struct svc_req *rqstp;
156 {
157 static bp_whoami_res res;
158 struct hostent *he;
159 struct in_addr haddr;
160 int e;
161
162 if (debug)
163 warnx("whoami got question for %d.%d.%d.%d",
164 255 & whoami->client_address.bp_address_u.ip_addr.net,
165 255 & whoami->client_address.bp_address_u.ip_addr.host,
166 255 & whoami->client_address.bp_address_u.ip_addr.lh,
167 255 & whoami->client_address.bp_address_u.ip_addr.impno);
168 if (dolog)
169 syslog(LOG_NOTICE, "whoami got question for %d.%d.%d.%d",
170 255 & whoami->client_address.bp_address_u.ip_addr.net,
171 255 & whoami->client_address.bp_address_u.ip_addr.host,
172 255 & whoami->client_address.bp_address_u.ip_addr.lh,
173 255 & whoami->client_address.bp_address_u.ip_addr.impno);
174
175 memmove((char *) &haddr,
176 (char *) &whoami->client_address.bp_address_u.ip_addr,
177 sizeof(haddr));
178 he = gethostbyaddr((char *) &haddr, sizeof(haddr), AF_INET);
179 if (he) {
180 strncpy(askname, he->h_name, sizeof(askname));
181 askname[sizeof(askname)-1] = 0;
182 } else {
183 strncpy(askname, inet_ntoa(haddr), sizeof(askname));
184 askname[sizeof(askname)-1] = 0;
185 }
186
187 if (debug)
188 warnx("This is host %s", askname);
189 if (dolog)
190 syslog(LOG_NOTICE, "This is host %s", askname);
191
192 if ((e = lookup_bootparam(askname, hostname, NULL, NULL, NULL)) == 0) {
193 res.client_name = hostname;
194 getdomainname(domain_name, MAX_MACHINE_NAME);
195 res.domain_name = domain_name;
196
197 if (res.router_address.address_type != IP_ADDR_TYPE) {
198 res.router_address.address_type = IP_ADDR_TYPE;
199 memmove(&res.router_address.bp_address_u.ip_addr,
200 &route_addr.s_addr,4);
201 }
202 if (debug)
203 warnx("Returning %s %s %d.%d.%d.%d",
204 res.client_name, res.domain_name,
205 255 & res.router_address.bp_address_u.ip_addr.net,
206 255 & res.router_address.bp_address_u.ip_addr.host,
207 255 & res.router_address.bp_address_u.ip_addr.lh,
208 255 &res.router_address.bp_address_u.ip_addr.impno);
209 if (dolog)
210 syslog(LOG_NOTICE, "Returning %s %s %d.%d.%d.%d",
211 res.client_name, res.domain_name,
212 255 & res.router_address.bp_address_u.ip_addr.net,
213 255 & res.router_address.bp_address_u.ip_addr.host,
214 255 & res.router_address.bp_address_u.ip_addr.lh,
215 255 &res.router_address.bp_address_u.ip_addr.impno);
216
217 return (&res);
218 }
219 errno = e;
220 if (debug)
221 warn("whoami failed");
222 if (dolog)
223 syslog(LOG_NOTICE, "whoami failed %m");
224 return (NULL);
225 }
226
227
228 bp_getfile_res *
229 bootparamproc_getfile_1_svc(getfile, rqstp)
230 bp_getfile_arg *getfile;
231 struct svc_req *rqstp;
232 {
233 static bp_getfile_res res;
234 struct hostent *he;
235 int err;
236
237 if (debug)
238 warnx("getfile got question for \"%s\" and file \"%s\"",
239 getfile->client_name, getfile->file_id);
240
241 if (dolog)
242 syslog(LOG_NOTICE,
243 "getfile got question for \"%s\" and file \"%s\"",
244 getfile->client_name, getfile->file_id);
245
246 he = NULL;
247 he = gethostbyname(getfile->client_name);
248 if (!he) {
249 if (debug)
250 warnx("getfile can't resolve client %s",
251 getfile->client_name);
252 if (dolog)
253 syslog(LOG_NOTICE, "getfile can't resolve client %s",
254 getfile->client_name);
255 return (NULL);
256 }
257
258 strncpy(askname, he->h_name, sizeof(askname));
259 askname[sizeof(askname)-1] = 0;
260 err = lookup_bootparam(askname, NULL, getfile->file_id,
261 &res.server_name, &res.server_path);
262 if (err == 0) {
263 he = gethostbyname(res.server_name);
264 if (!he) {
265 if (debug)
266 warnx("getfile can't resolve server %s for %s",
267 res.server_name, getfile->client_name);
268 if (dolog)
269 syslog(LOG_NOTICE,
270 "getfile can't resolve server %s for %s",
271 res.server_name, getfile->client_name);
272 return (NULL);
273
274 }
275 memmove(&res.server_address.bp_address_u.ip_addr,
276 he->h_addr, 4);
277 res.server_address.address_type = IP_ADDR_TYPE;
278 } else if (err == ENOENT && !strcmp(getfile->file_id, "dump")) {
279 /* Special for dump, answer with null strings. */
280 res.server_name[0] = '\0';
281 res.server_path[0] = '\0';
282 memset(&res.server_address.bp_address_u.ip_addr, 0, 4);
283 } else {
284 if (debug)
285 warnx("getfile lookup failed for %s",
286 getfile->client_name);
287 if (dolog)
288 syslog(LOG_NOTICE,
289 "getfile lookup failed for %s",
290 getfile->client_name);
291 return (NULL);
292 }
293
294 if (debug)
295 warnx(
296 "returning server:%s path:%s address: %d.%d.%d.%d",
297 res.server_name, res.server_path,
298 255 & res.server_address.bp_address_u.ip_addr.net,
299 255 & res.server_address.bp_address_u.ip_addr.host,
300 255 & res.server_address.bp_address_u.ip_addr.lh,
301 255 & res.server_address.bp_address_u.ip_addr.impno);
302 if (dolog)
303 syslog(LOG_NOTICE,
304 "returning server:%s path:%s address: %d.%d.%d.%d",
305 res.server_name, res.server_path,
306 255 & res.server_address.bp_address_u.ip_addr.net,
307 255 & res.server_address.bp_address_u.ip_addr.host,
308 255 & res.server_address.bp_address_u.ip_addr.lh,
309 255 & res.server_address.bp_address_u.ip_addr.impno);
310 return (&res);
311 }
312
313
314 int
315 lookup_bootparam(client, client_canonical, id, server, path)
316 char *client;
317 char *client_canonical;
318 char *id;
319 char **server;
320 char **path;
321 {
322 FILE *f = fopen(bootpfile, "r");
323 #ifdef YP
324 static char *ypbuf = NULL;
325 static int ypbuflen = 0;
326 #endif
327 static char buf[BUFSIZ];
328 char *canon = NULL, *bp, *word = NULL;
329 size_t idlen = id == NULL ? 0 : strlen(id);
330 int contin = 0;
331 int found = 0;
332
333 if (f == NULL)
334 return EINVAL; /* ? */
335
336 while (fgets(buf, sizeof buf, f)) {
337 int wascontin = contin;
338 contin = buf[strlen(buf) - 2] == '\\';
339 bp = buf + strspn(buf, " \t\n");
340
341 switch (wascontin) {
342 case -1:
343 /* Continuation of uninteresting line */
344 contin *= -1;
345 continue;
346 case 0:
347 /* New line */
348 contin *= -1;
349 if (*bp == '#')
350 continue;
351 if ((word = strsep(&bp, " \t\n")) == NULL)
352 continue;
353 #ifdef YP
354 /* A + in the file means try YP now */
355 if (!strcmp(word, "+")) {
356 char *ypdom;
357
358 if (yp_get_default_domain(&ypdom) ||
359 yp_match(ypdom, "bootparams", client,
360 strlen(client), &ypbuf, &ypbuflen))
361 continue;
362 bp = ypbuf;
363 word = client;
364 contin *= -1;
365 break;
366 }
367 #endif
368 if (debug)
369 warnx("match %s with %s", word, client);
370
371 #define HASGLOB(str) \
372 (strchr(str, '*') != NULL || \
373 strchr(str, '?') != NULL || \
374 strchr(str, '[') != NULL || \
375 strchr(str, ']') != NULL)
376
377 /* See if this line's client is the one we are
378 * looking for */
379 if (fnmatch(word, client, FNM_CASEFOLD) == 0) {
380 /*
381 * Match. The token may be globbed, we
382 * can't just return that as the canonical
383 * name. Check to see if the token has any
384 * globbing characters in it (*, ?, [, ]).
385 * If so, just return the name we already
386 * have. Otherwise, return the token.
387 */
388 if (HASGLOB(word))
389 canon = client;
390 else
391 canon = word;
392 } else {
393 struct hostent *hp;
394 /*
395 * If it didn't match, try getting the
396 * canonical host name of the client
397 * on this line, if it's not a glob,
398 * and comparing it to the client we
399 * are looking up.
400 */
401 if (HASGLOB(word)) {
402 if (debug)
403 warnx("Skipping non-match: %s",
404 word);
405 continue;
406 }
407 if ((hp = gethostbyname(word)) == NULL) {
408 if (debug)
409 warnx(
410 "Unknown bootparams host %s",
411 word);
412 if (dolog)
413 syslog(LOG_NOTICE,
414 "Unknown bootparams host %s",
415 word);
416 continue;
417 }
418 if (strcasecmp(hp->h_name, client) != 0)
419 continue;
420 canon = hp->h_name;
421 }
422
423 #undef HASGLOB
424
425 contin *= -1;
426 break;
427 case 1:
428 /* Continued line we want to parse below */
429 break;
430 }
431
432 assert(canon != NULL);
433 if (client_canonical)
434 strncpy(client_canonical, canon, MAX_MACHINE_NAME);
435
436 /* We have found a line for CLIENT */
437 if (id == NULL) {
438 (void) fclose(f);
439 return 0;
440 }
441
442 /* Look for a value for the parameter named by ID */
443 while ((word = strsep(&bp, " \t\n")) != NULL) {
444 if (!strncmp(word, id, idlen) && word[idlen] == '=') {
445 /* We have found the entry we want */
446 *server = &word[idlen + 1];
447 *path = strchr(*server, ':');
448 if (*path == NULL)
449 /* Malformed entry */
450 continue;
451 *(*path)++ = '\0';
452 (void) fclose(f);
453 return 0;
454 }
455 }
456
457 found = 1;
458 }
459
460 (void) fclose(f);
461 return found ? ENOENT : EPERM;
462 }
463
464 void
465 usage()
466 {
467 fprintf(stderr,
468 "usage: %s [-d] [-s] [-r router] [-f bootparmsfile]\n",
469 getprogname());
470 exit(1);
471 }
472
473 static int
474 get_localaddr(ifname, sin)
475 const char *ifname;
476 struct sockaddr_in *sin;
477 {
478 struct ifaddrs *ifap, *ifa;
479
480 if (getifaddrs(&ifap) != 0)
481 return -1;
482
483 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
484 if (ifname && strcmp(ifname, ifa->ifa_name) != 0)
485 continue;
486 if (ifa->ifa_addr->sa_family != AF_INET)
487 continue;
488 if (ifa->ifa_addr->sa_len != sizeof(*sin))
489 continue;
490
491 /* no loopback please */
492 #ifdef IFF_LOOPBACK
493 if (ifa->ifa_flags & IFF_LOOPBACK)
494 continue;
495 #else
496 if (strncmp(ifa->ifa_name, "lo", 2) == 0 &&
497 (isdigit(ifa->ifa_name[2]) || ifa->ifa_name[2] == '\0'))
498 continue;
499 #endif
500
501 /* XXX more sanity checks? */
502
503 /* candidate found */
504 memcpy(sin, ifa->ifa_addr, ifa->ifa_addr->sa_len);
505 freeifaddrs(ifap);
506 return 0;
507 }
508
509 /* no candidate */
510 freeifaddrs(ifap);
511 return -1;
512 }
513