1 /*
2 
3 This code is not copyright, and is placed in the public domain. Feel free to
4 use and modify. Please send modifications and/or suggestions + bug fixes to
5 
6         Klas Heggemann <klas@nada.kth.se>
7 
8 */
9 
10 #include <sys/cdefs.h>
11 __FBSDID("$FreeBSD$");
12 
13 #ifdef YP
14 #include <rpc/rpc.h>
15 #include <rpcsvc/yp_prot.h>
16 #include <rpcsvc/ypclnt.h>
17 #endif
18 #include "bootparam_prot.h"
19 #include <ctype.h>
20 #include <err.h>
21 #include <netdb.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <syslog.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 
29 extern int debug, dolog;
30 extern in_addr_t route_addr;
31 extern const char *bootpfile;
32 
33 #define MAXLEN 800
34 
35 static struct hostent *he;
36 static char buffer[MAXLEN];
37 static char hostname[MAX_MACHINE_NAME];
38 static char askname[MAX_MACHINE_NAME];
39 static char path[MAX_PATH_LEN];
40 static char domain_name[MAX_MACHINE_NAME];
41 
42 static int getthefile(char *, char *, char *, int);
43 static int checkhost(char *, char *, int);
44 
45 bp_whoami_res *
46 bootparamproc_whoami_1_svc(bp_whoami_arg *whoami, struct svc_req *req __unused)
47 {
48   in_addr_t haddr;
49   static bp_whoami_res res;
50   if (debug)
51     fprintf(stderr,"whoami got question for %d.%d.%d.%d\n",
52 	    255 &  whoami->client_address.bp_address_u.ip_addr.net,
53 	    255 & whoami->client_address.bp_address_u.ip_addr.host,
54 	    255 &  whoami->client_address.bp_address_u.ip_addr.lh,
55 	    255 &  whoami->client_address.bp_address_u.ip_addr.impno);
56   if (dolog)
57     syslog(LOG_NOTICE, "whoami got question for %d.%d.%d.%d\n",
58 	    255 &  whoami->client_address.bp_address_u.ip_addr.net,
59 	    255 & whoami->client_address.bp_address_u.ip_addr.host,
60 	    255 &  whoami->client_address.bp_address_u.ip_addr.lh,
61 	    255 &  whoami->client_address.bp_address_u.ip_addr.impno);
62 
63   bcopy((char *)&whoami->client_address.bp_address_u.ip_addr, (char *)&haddr,
64 	sizeof(haddr));
65   he = gethostbyaddr((char *)&haddr,sizeof(haddr),AF_INET);
66   if ( ! he ) goto failed;
67 
68   if (debug) warnx("this is host %s", he->h_name);
69   if (dolog) syslog(LOG_NOTICE,"This is host %s\n", he->h_name);
70 
71   strncpy(askname, he->h_name, sizeof(askname));
72   askname[sizeof(askname)-1] = 0;
73 
74   if (checkhost(askname, hostname, sizeof hostname) ) {
75     res.client_name = hostname;
76     getdomainname(domain_name, MAX_MACHINE_NAME);
77     res.domain_name = domain_name;
78 
79     if (  res.router_address.address_type != IP_ADDR_TYPE ) {
80       res.router_address.address_type = IP_ADDR_TYPE;
81       bcopy( &route_addr, &res.router_address.bp_address_u.ip_addr, sizeof(in_addr_t));
82     }
83     if (debug) fprintf(stderr,
84 		       "Returning %s   %s    %d.%d.%d.%d\n",
85 		       res.client_name,
86 		       res.domain_name,
87 		       255 &  res.router_address.bp_address_u.ip_addr.net,
88 		       255 & res.router_address.bp_address_u.ip_addr.host,
89 		       255 &  res.router_address.bp_address_u.ip_addr.lh,
90 		       255 & res.router_address.bp_address_u.ip_addr.impno);
91     if (dolog) syslog(LOG_NOTICE,
92 		       "Returning %s   %s    %d.%d.%d.%d\n",
93 		       res.client_name,
94 		       res.domain_name,
95 		       255 &  res.router_address.bp_address_u.ip_addr.net,
96 		       255 & res.router_address.bp_address_u.ip_addr.host,
97 		       255 &  res.router_address.bp_address_u.ip_addr.lh,
98 		       255 & res.router_address.bp_address_u.ip_addr.impno);
99 
100     return(&res);
101   }
102  failed:
103   if (debug) warnx("whoami failed");
104   if (dolog) syslog(LOG_NOTICE,"whoami failed\n");
105   return(NULL);
106 }
107 
108 
109 bp_getfile_res *
110 bootparamproc_getfile_1_svc(bp_getfile_arg *getfile, struct svc_req *req __unused)
111 {
112   char *where;
113   static bp_getfile_res res;
114 
115   if (debug)
116     warnx("getfile got question for \"%s\" and file \"%s\"",
117 	    getfile->client_name, getfile->file_id);
118 
119   if (dolog)
120     syslog(LOG_NOTICE,"getfile got question for \"%s\" and file \"%s\"\n",
121 	    getfile->client_name, getfile->file_id);
122 
123   he = NULL;
124   he = gethostbyname(getfile->client_name);
125   if (! he ) goto failed;
126 
127   strncpy(askname, he->h_name, sizeof(askname));
128   askname[sizeof(askname)-1] = 0;
129 
130   if (getthefile(askname, getfile->file_id,buffer,sizeof(buffer))) {
131     if ( (where = strchr(buffer,':')) ) {
132       /* buffer is re-written to contain the name of the info of file */
133       strncpy(hostname, buffer, where - buffer);
134       hostname[where - buffer] = '\0';
135       where++;
136       strcpy(path, where);
137       he = gethostbyname(hostname);
138       if ( !he ) goto failed;
139       bcopy( he->h_addr, &res.server_address.bp_address_u.ip_addr, 4);
140       res.server_name = hostname;
141       res.server_path = path;
142       res.server_address.address_type = IP_ADDR_TYPE;
143     }
144     else { /* special for dump, answer with null strings */
145       if (!strcmp(getfile->file_id, "dump")) {
146 	res.server_name = "";
147 	res.server_path = "";
148         res.server_address.address_type = IP_ADDR_TYPE;
149 	bzero(&res.server_address.bp_address_u.ip_addr,4);
150       } else goto failed;
151     }
152     if (debug)
153       fprintf(stderr, "returning server:%s path:%s address: %d.%d.%d.%d\n",
154 	     res.server_name, res.server_path,
155 	     255 &  res.server_address.bp_address_u.ip_addr.net,
156 	     255 & res.server_address.bp_address_u.ip_addr.host,
157 	     255 &  res.server_address.bp_address_u.ip_addr.lh,
158 	     255 & res.server_address.bp_address_u.ip_addr.impno);
159     if (dolog)
160       syslog(LOG_NOTICE, "returning server:%s path:%s address: %d.%d.%d.%d\n",
161 	     res.server_name, res.server_path,
162 	     255 &  res.server_address.bp_address_u.ip_addr.net,
163 	     255 & res.server_address.bp_address_u.ip_addr.host,
164 	     255 &  res.server_address.bp_address_u.ip_addr.lh,
165 	     255 & res.server_address.bp_address_u.ip_addr.impno);
166     return(&res);
167   }
168   failed:
169   if (debug) warnx("getfile failed for %s", getfile->client_name);
170   if (dolog) syslog(LOG_NOTICE,
171 		    "getfile failed for %s\n", getfile->client_name);
172   return(NULL);
173 }
174 
175 /*    getthefile return 1 and fills the buf with the information
176       of the file, e g "host:/export/root/client" if it can be found.
177       If the host is in the database, but the file is not, the buf
178       will be empty. (This makes it possible to give the special
179       empty answer for the file "dump")   */
180 
181 static int
182 getthefile(char *l_askname, char *fileid, char *buf, int blen __unused)
183 {
184   FILE *bpf;
185   char  *where;
186 #ifdef YP
187   static char *result;
188   int resultlen;
189   static char *yp_domain;
190 #endif
191 
192   int ch, pch, fid_len, res = 0;
193   int match = 0;
194 #define INFOLEN 1343
195   _Static_assert(INFOLEN >= MAX_FILEID + MAX_PATH_LEN+MAX_MACHINE_NAME + 3,
196 		  "INFOLEN isn't large enough");
197   char info[INFOLEN + 1];
198 
199   bpf = fopen(bootpfile, "r");
200   if ( ! bpf )
201     errx(1, "no %s", bootpfile);
202 
203   /* XXX see comment below */
204   while ( fscanf(bpf, "%255s", hostname) > 0  && !match ) {
205     if ( *hostname != '#' ) { /* comment */
206       if ( ! strcmp(hostname, l_askname) ) {
207 	match = 1;
208       } else {
209 	he = gethostbyname(hostname);
210 	if (he && !strcmp(he->h_name, l_askname)) match = 1;
211       }
212     }
213     if (*hostname == '+' ) { /* NIS */
214 #ifdef YP
215       if (yp_get_default_domain(&yp_domain)) {
216 	 if (debug) warn("NIS");
217 	 return(0);
218       }
219       if (yp_match(yp_domain, "bootparams", l_askname, strlen(l_askname),
220 		&result, &resultlen))
221 	return (0);
222       if (strstr(result, fileid) == NULL) {
223 	buf[0] = '\0';
224       } else {
225 	snprintf(buf, blen,
226 		"%s",strchr(strstr(result,fileid), '=') + 1);
227 	if (strchr(buf, ' ') != NULL)
228 	  *(char *)(strchr(buf, ' ')) = '\0';
229       }
230       if (fclose(bpf))
231         warnx("could not close %s", bootpfile);
232       return(1);
233 #else
234       if (fclose(bpf))
235         warnx("could not close %s", bootpfile);
236       return(0);	/* ENOTSUP */
237 #endif
238     }
239     /* skip to next entry */
240     if ( match ) break;
241     pch = ch = getc(bpf);
242     while ( ! ( ch == '\n' && pch != '\\') && ch != EOF) {
243       pch = ch; ch = getc(bpf);
244     }
245   }
246 
247   /* if match is true we read the rest of the line to get the
248      info of the file */
249 
250   if (match) {
251     fid_len = strlen(fileid);
252 #define AS_FORMAT(d)	"%" #d "s"
253 #define REXPAND(d) AS_FORMAT(d)	/* Force another preprocessor expansion */
254     while ( ! res && (fscanf(bpf, REXPAND(INFOLEN), info)) > 0) {
255       ch = getc(bpf);                                /* and a character */
256       if ( *info != '#' ) {                          /* Comment ? */
257 	if (! strncmp(info, fileid, fid_len) && *(info + fid_len) == '=') {
258 	  where = info + fid_len + 1;
259 	  if ( isprint( *where )) {
260 	    strcpy(buf, where);                   /* found file */
261 	    res = 1; break;
262 	  }
263 	} else {
264 	  while (isspace(ch) && ch != '\n') ch = getc(bpf);
265 	                                             /* read to end of line */
266 	  if ( ch == '\n' ) {                        /* didn't find it */
267 	    res = -1; break;                         /* but host is there */
268 	  }
269 	  if ( ch == '\\' ) {                        /* more info */
270 	    ch = getc(bpf);                          /* maybe on next line */
271 	    if (ch == '\n') continue;                /* read it in next loop */
272 	    ungetc(ch, bpf); ungetc('\\',bpf); /* push the character(s) back */
273 	  } else ungetc(ch, bpf);              /* but who know what a `\` is */
274 	}                                      /* needed for. */
275       } else break;                            /* a commented rest-of-line */
276     }
277   }
278   if (fclose(bpf)) { warnx("could not close %s", bootpfile); }
279   if ( res == -1) buf[0] = '\0';            /* host found, file not */
280   return(match);
281 }
282 
283 /* checkhost puts the hostname found in the database file in
284    the l_hostname-variable and returns 1, if l_askname is a valid
285    name for a host in the database */
286 
287 static int
288 checkhost(char *l_askname, char *l_hostname, int len __unused)
289 {
290   int ch, pch;
291   FILE *bpf;
292   int res = 0;
293 #ifdef YP
294   static char *result;
295   int resultlen;
296   static char *yp_domain;
297 #endif
298 
299 /*  struct hostent *cmp_he;*/
300 
301   bpf = fopen(bootpfile, "r");
302   if ( ! bpf )
303     errx(1, "no %s", bootpfile);
304 
305   /* XXX there is no way in ISO C to specify the maximal length for a
306      conversion in a variable way */
307   while ( fscanf(bpf, "%254s", l_hostname) > 0 ) {
308     if ( *l_hostname != '#' ) { /* comment */
309       if ( ! strcmp(l_hostname, l_askname) ) {
310         /* return true for match of l_hostname */
311         res = 1;
312         break;
313       } else {
314         /* check the alias list */
315         he = NULL;
316         he = gethostbyname(l_hostname);
317         if (he && !strcmp(l_askname, he->h_name)) {
318   	  res = 1;
319 	  break;
320         }
321       }
322     }
323     if (*l_hostname == '+' ) { /* NIS */
324 #ifdef YP
325       if (yp_get_default_domain(&yp_domain)) {
326 	 if (debug) warn("NIS");
327 	 return(0);
328       }
329       if (!yp_match(yp_domain, "bootparams", l_askname, strlen(l_askname),
330 		&result, &resultlen)) {
331         /* return true for match of hostname */
332         he = NULL;
333         he = gethostbyname(l_askname);
334         if (he && !strcmp(l_askname, he->h_name)) {
335   	  res = 1;
336 	  snprintf(l_hostname, len, "%s", he->h_name);
337 	}
338       }
339       if (fclose(bpf))
340         warnx("could not close %s", bootpfile);
341       return(res);
342 #else
343       return(0);	/* ENOTSUP */
344 #endif
345     }
346     /* skip to next entry */
347     pch = ch = getc(bpf);
348     while ( ! ( ch == '\n' && pch != '\\') && ch != EOF) {
349       pch = ch; ch = getc(bpf);
350     }
351   }
352   if (fclose(bpf)) { warnx("could not close %s", bootpfile); }
353   return(res);
354 }
355