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