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