• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2010-2012 Gene Cumm - All Rights Reserved
4  *
5  *   Portions from chain.c:
6  *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
7  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
8  *   Significant portions copyright (C) 2010 Shao Miller
9  *					[partition iteration, GPT, "fs"]
10  *
11  *   This program is free software; you can redistribute it and/or modify
12  *   it under the terms of the GNU General Public License as published by
13  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
14  *   Boston MA 02111-1307, USA; either version 2 of the License, or
15  *   (at your option) any later version; incorporated herein by reference.
16  *
17  * ----------------------------------------------------------------------- */
18 
19 /*
20  * pxechn.c
21  *
22  * PXE Chain Loader; Chain load to another PXE network boot program
23  * that may be on another host.
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <consoles.h>
29 #include <console.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <syslinux/config.h>
33 #include <syslinux/loadfile.h>
34 #include <syslinux/bootrm.h>
35 #include <syslinux/video.h>
36 #include <com32.h>
37 #include <stdint.h>
38 #include <syslinux/pxe.h>
39 #include <sys/gpxe.h>
40 #include <unistd.h>
41 #include <getkey.h>
42 #include <dhcp.h>
43 #include <limits.h>
44 
45 
46 #ifdef DEBUG
47 #  define PXECHN_DEBUG 1
48 #else
49 #  define PXECHN_DEBUG 0
50 #endif
51 
52 typedef union {
53     uint64_t q;
54     uint32_t l[2];
55     uint16_t w[4];
56     uint8_t b[8];
57 } reg64_t;
58 
59 #define dprintf0(f, ...)		((void)0)
60 
61 #ifndef dprintf
62 #  if (PXECHN_DEBUG > 0)
63 #    define dprintf			printf
64 #  else
65 #    define dprintf(f, ...)		((void)0)
66 #  endif
67 #endif
68 
69 #if (PXECHN_DEBUG > 0)
70 #  define dpressanykey			pressanykey
71 #  define dprint_pxe_bootp_t		print_pxe_bootp_t
72 #  define dprint_pxe_vendor_blk		print_pxe_vendor_blk
73 #  define dprint_pxe_vendor_raw		print_pxe_vendor_raw
74 #else
75 #  define dpressanykey(tm)		((void)0)
76 #  define dprint_pxe_bootp_t(p, l)	((void)0)
77 #  define dprint_pxe_vendor_blk(p, l)	((void)0)
78 #  define dprint_pxe_vendor_raw(p, l)	((void)0)
79 #endif
80 
81 #define dprintf_opt_cp		dprintf0
82 #define dprintf_opt_inj		dprintf0
83 #define dprintf_pc_pa		dprintf
84 #define dprintf_pc_so_s		dprintf0
85 
86 #define t_PXENV_RESTART_TFTP	t_PXENV_TFTP_READ_FILE
87 
88 #define STACK_SPLIT	11
89 
90 /* same as pxelinux.asm REBOOT_TIME */
91 #define REBOOT_TIME	300
92 
93 #define NUM_DHCP_OPTS		256
94 #define DHCP_OPT_LEN_MAX	256
95 #define PXE_VENDOR_RAW_PRN_MAX	0x7F
96 #define PXECHN_HOST_LEN		256	/* 63 bytes per label; 255 max total */
97 
98 #define PXECHN_NUM_PKT_TYPE	3
99 #define PXECHN_NUM_PKT_AVAIL	2*PXECHN_NUM_PKT_TYPE
100 #define PXECHN_PKT_TYPE_START	PXENV_PACKET_TYPE_DHCP_DISCOVER
101 
102 #define PXECHN_FORCE_PKT1	0x80000000
103 #define PXECHN_FORCE_PKT2	0x40000000
104 #define PXECHN_FORCE_ALL	(PXECHN_FORCE_PKT1 | PXECHN_FORCE_PKT2)
105 #define PXECHN_FORCE_ALL_1	0
106 #define STRASINT_str		('s' + (('t' + ('r' << 8)) << 8))
107 
108 #define min(a,b) (((a) < (b)) ? (a) : (b))
109 
110 const char app_name_str[] = "pxechn.c32";
111 
112 struct pxelinux_opt {
113     char *fn;	/* Filename as passed to us */
114     in_addr_t fip;	/* fn's IP component */
115     char *fp;	/* fn's path component */
116     in_addr_t gip;	/* giaddr; Gateway/DHCP relay */
117     uint32_t force;
118     uint32_t wait;	/* Additional decision to wait before boot */
119     int32_t wds;	/* WDS option/level */
120     in_addr_t sip;	/* siaddr: Next Server IP Address */
121     struct dhcp_option p[PXECHN_NUM_PKT_AVAIL];
122 	/* original _DHCP_DISCOVER, _DHCP_ACK, _CACHED_REPLY then modified packets */
123     char host[PXECHN_HOST_LEN];
124     struct dhcp_option opts[PXECHN_NUM_PKT_TYPE][NUM_DHCP_OPTS];
125     char p_unpacked[PXECHN_NUM_PKT_TYPE];
126 };
127 
128 
129 /* from chain.c */
130 struct data_area {
131     void *data;
132     addr_t base;
133     addr_t size;
134 };
135 
136 /* From chain.c */
error(const char * msg)137 static inline void error(const char *msg)
138 {
139     fputs(msg, stderr);
140 }
141 
142 /* From chain.c */
do_boot(struct data_area * data,int ndata,struct syslinux_rm_regs * regs)143 static void do_boot(struct data_area *data, int ndata,
144 		    struct syslinux_rm_regs *regs)
145 {
146     uint16_t *const bios_fbm = (uint16_t *) 0x413;
147     addr_t dosmem = *bios_fbm << 10;	/* Technically a low bound */
148     struct syslinux_memmap *mmap;
149     struct syslinux_movelist *mlist = NULL;
150     addr_t endimage;
151     int i;
152 
153     mmap = syslinux_memory_map();
154 
155     if (!mmap) {
156 	error("Cannot read system memory map\n");
157 	return;
158     }
159 
160     endimage = 0;
161     for (i = 0; i < ndata; i++) {
162 	if (data[i].base + data[i].size > endimage)
163 	    endimage = data[i].base + data[i].size;
164     }
165     if (endimage > dosmem)
166 	goto too_big;
167 
168     for (i = 0; i < ndata; i++) {
169 	if (syslinux_add_movelist(&mlist, data[i].base,
170 				  (addr_t) data[i].data, data[i].size))
171 	    goto enomem;
172     }
173 
174 
175     /* Tell the shuffler not to muck with this area... */
176     syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
177 
178     /* Force text mode */
179     syslinux_force_text_mode();
180 
181     fputs("Booting...\n", stdout);
182     syslinux_shuffle_boot_rm(mlist, mmap, 3, regs);
183     error("Chainboot failed!\n");
184     return;
185 
186 too_big:
187     error("Loader file too large\n");
188     return;
189 
190 enomem:
191     error("Out of memory\n");
192     return;
193 }
194 
usage(void)195 void usage(void)
196 {
197     printf("USAGE:\n"
198         "    %s [OPTIONS]... _new-nbp_\n"
199 	"    %s -r _new-nbp_    (calls PXE stack PXENV_RESTART_TFTP)\n"
200 	"OPTIONS:\n"
201 	"    [-c config] [-g gateway] [-p prefix] [-t reboot] [-u] [-w] [-W]"
202 	" [-o opt.ty=val]\n\n",
203 	app_name_str, app_name_str);
204 }
205 
pxe_error(int ierr,const char * evt,const char * msg)206 void pxe_error(int ierr, const char *evt, const char *msg)
207 {
208     if (msg)
209 	printf("%s", msg);
210     else if (evt)
211 	printf("Error while %s: ", evt);
212     printf("%d:%s\n", ierr, strerror(ierr));
213 }
214 
pressanykey(clock_t tm)215 int pressanykey(clock_t tm) {
216     int inc;
217 
218     printf("Press any key to continue. ");
219     inc = get_key(stdin, tm);
220     puts("");
221     return inc;
222 }
223 
dhcp_find_opt(pxe_bootp_t * p,size_t len,uint8_t opt)224 int dhcp_find_opt(pxe_bootp_t *p, size_t len, uint8_t opt)
225 {
226     int rv = -1;
227     int i, vlen, oplen;
228     uint8_t *d;
229     uint32_t magic;
230 
231     if (!p) {
232 	dprintf("  packet pointer is null\n");
233 	return rv;
234     }
235     vlen = len - ((void *)&(p->vendor) - (void *)p);
236     d = p->vendor.d;
237     magic = ntohl(*((uint32_t *)d));
238     if (magic != VM_RFC1048)	/* Invalid DHCP packet */
239 	vlen = 0;
240     for (i = 4; i < vlen; i++) {
241 	if (d[i] == opt) {
242 	    dprintf("\n    @%03X-%2d\n", i, d[i]);
243 	    rv = i;
244 	    break;
245 	}
246 	if (d[i] == ((NUM_DHCP_OPTS) - 1))	/* End of list */
247 	    break;
248 	if (d[i]) {		/* Skip padding */
249 	    oplen = d[++i];
250 	    i += oplen;
251 	}
252     }
253     return rv;
254 }
255 
print_pxe_vendor_raw(pxe_bootp_t * p,size_t len)256 void print_pxe_vendor_raw(pxe_bootp_t *p, size_t len)
257 {
258     int i, vlen;
259 
260     if (!p) {
261 	printf("  packet pointer is null\n");
262 	return;
263     }
264     vlen = len - ((void *)&(p->vendor) - (void *)p);
265     if (vlen > PXE_VENDOR_RAW_PRN_MAX)
266 	vlen = PXE_VENDOR_RAW_PRN_MAX;
267     dprintf("  rawLen = %d", vlen);
268     for (i = 0; i < vlen; i++) {
269 	if ((i & 0xf) == 0)
270 	    printf("\n  %04X:", i);
271 	printf(" %02X", p->vendor.d[i]);
272     }
273     printf("\n");
274 }
275 
print_pxe_vendor_blk(pxe_bootp_t * p,size_t len)276 void print_pxe_vendor_blk(pxe_bootp_t *p, size_t len)
277 {
278     int i, vlen, oplen, j;
279     uint8_t *d;
280     uint32_t magic;
281     if (!p) {
282 	printf("  packet pointer is null\n");
283 	return;
284     }
285     vlen = len - ((void *)&(p->vendor) - (void *)p);
286     printf("  Vendor Data:    Len=%d", vlen);
287     d = p->vendor.d;
288     magic = ntohl(*((uint32_t *)d));
289     printf("    Magic: %08X", ntohl(*((uint32_t *)d)));
290     if (magic != VM_RFC1048)	/* Invalid DHCP packet */
291 	vlen = 0;
292     for (i = 4; i < vlen; i++) {
293 	if (d[i])	/* Skip the padding */
294 	    printf("\n    @%03X-%3d", i, d[i]);
295 	if (d[i] == ((NUM_DHCP_OPTS) - 1))	/* End of list */
296 	    break;
297 	if (d[i]) {
298 	    oplen = d[++i];
299 	    printf(" l=%3d:", oplen);
300 	    for (j = (++i + oplen); i < vlen && i < j; i++) {
301 		printf(" %02X", d[i]);
302 	    }
303 	    i--;
304 	}
305     }
306     printf("\n");
307 }
308 
print_pxe_bootp_t(pxe_bootp_t * p,size_t len)309 void print_pxe_bootp_t(pxe_bootp_t *p, size_t len)
310 {
311     if (!p || len <= 0) {
312 	printf("  packet pointer is null\n");
313 	return;
314     }
315     printf("  op:%02X  hw:%02X  hl:%02X  gh:%02X  id:%08X se:%04X f:%04X"
316 	"  cip:%08X\n", p->opcode, p->Hardware, p->Hardlen, p->Gatehops,
317 	ntohl(p->ident), ntohs(p->seconds), ntohs(p->Flags), ntohl(p->cip));
318     printf("  yip:%08X  sip:%08X  gip:%08X",
319 	ntohl(p->yip), ntohl(p->sip), ntohl(p->gip));
320     printf("  caddr-%02X:%02X:%02X:%02X:%02X:%02X\n", p->CAddr[0],
321 	p->CAddr[1], p->CAddr[2], p->CAddr[3], p->CAddr[4], p->CAddr[5]);
322     printf("  sName: '%s'\n", p->Sname);
323     printf("  bootfile: '%s'\n", p->bootfile);
324     dprint_pxe_vendor_blk(p, len);
325 }
326 
pxe_set_regs(struct syslinux_rm_regs * regs)327 void pxe_set_regs(struct syslinux_rm_regs *regs)
328 {
329     const union syslinux_derivative_info *sdi;
330     const com32sys_t *pxe_regs;
331 
332     sdi = syslinux_derivative_info();
333     pxe_regs = sdi->pxe.stack;	/* Original register values */
334 
335     /* Just to be sure... */
336     memset(regs, 0, sizeof *regs);
337 
338     regs->ip = 0x7C00;
339 
340     /* Point to the original stack */
341     regs->ss    = sdi->pxe.stack_seg;
342     regs->esp.l = sdi->pxe.stack_offs + sizeof(com32sys_t);
343 
344     /* Point to the PXENV+ address */
345     regs->es    = pxe_regs->es;
346     regs->ebx.l = pxe_regs->ebx.l;
347 
348     dprintf("\nsp:%04x    ss:%04x    es:%04x    bx:%04x\n", regs->esp.w[0],
349 	regs->ss, regs->es, regs->ebx.w[0]);
350 }
351 
hostlen_limit(int len)352 int hostlen_limit(int len)
353 {
354     return min(len, ((PXECHN_HOST_LEN) - 1));
355 }
356 
357 //FIXME: To a library
358 /* Parse a filename into an IPv4 address and filename pointer
359  *	returns	Based on the interpretation of fn
360  *		0 regular file name
361  *		1 in format IP::FN
362  *		2 TFTP URL
363  *		3 HTTP URL
364  *		4 FTP URL
365  *		3 + 2^30 HTTPS URL
366  *		-1 if fn is another URL type
367  */
pxechn_parse_fn(char fn[],in_addr_t * fip,char * host,char * fp[])368 int pxechn_parse_fn(char fn[], in_addr_t *fip, char *host, char *fp[])
369 {
370     in_addr_t tip = 0;
371     char *csep, *ssep, *hsep;	/* Colon, Slash separator positions */
372     int hlen, plen;	/* Hostname, protocol length */
373     int rv = 0;
374 
375     csep = strchr(fn, ':');
376     if (csep) {
377 	if (csep[1] == ':') {	/* assume IP::FN */
378 	    *fp = &csep[2];
379 	    rv = 1;
380 	    if (fn[0] != ':') {
381 		hlen = hostlen_limit(csep - fn);
382 		memcpy(host, fn, hlen);
383 		host[hlen] = 0;
384 	    }
385 	} else if ((csep[1] == '/') && (csep[2] == '/')) {
386 		/* URL: proto://host:port/path/file */
387 		/* proto://[user[:passwd]@]host[:port]/path/file */
388 	    ssep = strchr(csep + 3, '/');
389 	    if (ssep) {
390 		hlen = hostlen_limit(ssep - (csep + 3));
391 		*fp = ssep + 1;
392 	    } else {
393 		hlen = hostlen_limit(strlen(csep + 3));
394 	    }
395 	    memcpy(host, (csep + 3), hlen);
396 	    host[hlen] = 0;
397 	    plen = csep - fn;
398 	    if (strncmp(fn, "tftp", plen) == 0)
399 		rv = 2;
400 	    else if (strncmp(fn, "http", plen) == 0)
401 		rv = 3;
402 	    else if (strncmp(fn, "ftp", plen) == 0)
403 		rv = 4;
404 	    else if (strncmp(fn, "https", plen) == 0)
405 		rv = 3 + ( 1 << 30 );
406 	    else
407 		rv = -1;
408 	} else {
409 	    csep = NULL;
410 	}
411     }
412     if (!csep) {
413 	*fp = fn;
414     }
415     if (host[0]) {
416 	hsep = strchr(host, '@');
417 	if (!hsep)
418 	    hsep = host;
419 	tip = pxe_dns(hsep);
420     }
421     if (tip != 0)
422 	*fip = tip;
423     dprintf0("  host '%s'\n  fp   '%s'\n  fip  %08x\n", host, *fp, ntohl(*fip));
424     return rv;
425 }
426 
pxechn_opt_free(struct dhcp_option * opt)427 void pxechn_opt_free(struct dhcp_option *opt)
428 {
429     free(opt->data);
430     opt->len = -1;
431 }
432 
pxechn_fill_pkt(struct pxelinux_opt * pxe,int ptype)433 void pxechn_fill_pkt(struct pxelinux_opt *pxe, int ptype)
434 {
435     int rv = -1;
436     int p1, p2;
437     if ((ptype < 0) || (ptype > PXECHN_NUM_PKT_TYPE))
438 	rv = -2;
439     p1 = ptype - PXECHN_PKT_TYPE_START;
440     p2 = p1 + PXECHN_NUM_PKT_TYPE;
441     if ((rv >= -1) && (!pxe_get_cached_info(ptype,
442 	    (void **)&(pxe->p[p1].data), (size_t *)&(pxe->p[p1].len)))) {
443 	pxe->p[p2].data = malloc(2048);
444 	if (pxe->p[p2].data) {
445 	    memcpy(pxe->p[p2].data, pxe->p[p1].data, pxe->p[p1].len);
446 	    pxe->p[p2].len = pxe->p[p1].len;
447 	    rv = 0;
448 	    dprint_pxe_bootp_t((pxe_bootp_t *)(pxe->p[p1].data), pxe->p[p1].len);
449 	    dpressanykey(INT_MAX);
450 	} else {
451 	    printf("%s: ERROR: Unable to malloc() for second packet\n", app_name_str);
452 	}
453     } else {
454 	printf("%s: ERROR: Unable to retrieve first packet\n", app_name_str);
455     }
456     if (rv <= -1) {
457 	pxechn_opt_free(&pxe->p[p1]);
458     }
459 }
460 
pxechn_init(struct pxelinux_opt * pxe)461 void pxechn_init(struct pxelinux_opt *pxe)
462 {
463     /* Init for paranoia */
464     pxe->fn = NULL;
465     pxe->fp = NULL;
466     pxe->force = 0;
467     pxe->wait = 0;
468     pxe->gip = 0;
469     pxe->wds = 0;
470     pxe->sip = 0;
471     pxe->host[0] = 0;
472     pxe->host[((NUM_DHCP_OPTS) - 1)] = 0;
473     for (int j = 0; j < PXECHN_NUM_PKT_TYPE; j++){
474 	for (int i = 0; i < NUM_DHCP_OPTS; i++) {
475 	    pxe->opts[j][i].data = NULL;
476 	    pxe->opts[j][i].len = -1;
477 	}
478 	pxe->p_unpacked[j] = 0;
479 	pxe->p[j].data = NULL;
480 	pxe->p[j+PXECHN_NUM_PKT_TYPE].data = NULL;
481 	pxe->p[j].len = 0;
482 	pxe->p[j+PXECHN_NUM_PKT_TYPE].len = 0;
483     }
484     pxechn_fill_pkt(pxe, PXENV_PACKET_TYPE_CACHED_REPLY);
485 }
486 
pxechn_to_hex(char i)487 int pxechn_to_hex(char i)
488 {
489     if (i >= '0' && i <= '9')
490 	return (i - '0');
491     if (i >= 'A' && i <= 'F')
492 	return (i - 'A' + 10);
493     if (i >= 'a' && i <= 'f')
494 	return (i - 'a' + 10);
495     if (i == 0)
496 	return -1;
497     return -2;
498 }
499 
pxechn_parse_2bhex(char ins[])500 int pxechn_parse_2bhex(char ins[])
501 {
502     int ret = -2;
503     int n0 = -3, n1 = -3;
504     /* NULL pointer */
505     if (!ins) {
506 	ret = -1;
507     /* pxechn_to_hex can handle the NULL character by returning -1 and
508        breaking the execution of the statement chain */
509     } else if (((n0 = pxechn_to_hex(ins[0])) >= 0)
510 	    && ((n1 = pxechn_to_hex(ins[1])) >= 0)) {
511 	ret = (n0 * 16) + n1;
512     } else if (n0 == -1) {	/* Leading NULL char */
513 	ret = -1;
514     }
515     return ret;
516 }
517 
pxechn_optnum_ok(int optnum)518 int pxechn_optnum_ok(int optnum)
519 {
520     if ((optnum > 0) && (optnum < ((NUM_DHCP_OPTS) - 1)))
521 	return 1;
522     return 0;
523 }
524 
pxechn_optnum_ok_notres(int optnum)525 int pxechn_optnum_ok_notres(int optnum)
526 {
527     if ((optnum <= 0) && (optnum >= ((NUM_DHCP_OPTS) - 1)))
528 	return 0;
529     switch(optnum){
530     case 66: case 67:
531 	return 0;
532 	break;
533     default:	return 1;
534     }
535 }
536 
pxechn_optlen_ok(int optlen)537 int pxechn_optlen_ok(int optlen)
538 {
539     if ((optlen >= 0) && (optlen < ((DHCP_OPT_LEN_MAX) - 1)))
540 	return 1;
541     return 0;
542 }
543 
pxechn_setopt(struct dhcp_option * opt,void * data,int len)544 int pxechn_setopt(struct dhcp_option *opt, void *data, int len)
545 {
546     void *p;
547     if (!opt || !data)
548 	return -1;
549     if (len < 0) {
550 	return -3;
551     }
552     p = realloc(opt->data, len);
553     if (!p && len) {	/* Allow for len=0 */
554 	pxechn_opt_free(opt);
555 	return -2;
556     }
557     opt->data = p;
558     memcpy(opt->data, data, len);
559     opt->len = len;
560     return len;
561 }
562 
pxechn_setopt_str(struct dhcp_option * opt,void * data)563 int pxechn_setopt_str(struct dhcp_option *opt, void *data)
564 {
565     return pxechn_setopt(opt, data, strnlen(data, DHCP_OPT_LEN_MAX));
566 }
567 
pxechn_parse_int(char * data,char istr[],int tlen)568 int pxechn_parse_int(char *data, char istr[], int tlen)
569 {
570     int terr = errno;
571 
572     if ((tlen == 1) || (tlen == 2) || (tlen == 4)) {
573 	errno = 0;
574 	uint32_t optval = strtoul(istr, NULL, 0);
575 	if (errno)
576 	    return -3;
577 	errno = terr;
578 	switch(tlen){
579 	case  1:
580 	    if (optval & 0xFFFFFF00)
581 		return -4;
582 	    break;
583 	case  2:
584 	    if (optval & 0xFFFF0000)
585 		return -4;
586 	    optval = htons(optval);
587 	    break;
588 	case  4:
589 	    optval = htonl(optval);
590 	    break;
591 	}
592 	memcpy(data, &optval, tlen);
593     } else if (tlen == 8) {
594 	errno = 0;
595 	uint64_t optval = strtoull(istr, NULL, 0);
596 	if (errno)
597 	    return -3;
598 	errno = terr;
599 	optval = htonq(optval);
600 	memcpy(data, &optval, tlen);
601     } else {
602 	return -2;
603     }
604     return tlen;
605 }
606 
pxechn_parse_hex_sep(char * data,char istr[],char sep)607 int pxechn_parse_hex_sep(char *data, char istr[], char sep)
608 {
609     int len = 0;
610     int ipos = 0, ichar;
611 
612     if (!data || !istr)
613 	return -1;
614     while ((istr[ipos]) && (len < DHCP_OPT_LEN_MAX)) {
615 	dprintf(" %02X%02X", *((int *)(istr + ipos)) & 0xFF, *((int *)(istr + ipos +1)) & 0xFF);
616 	ichar = pxechn_parse_2bhex(istr + ipos);
617 	if (ichar >=0) {
618 	    data[len++] = ichar;
619 	} else {
620 	    return -EINVAL;
621 	}
622 	if (!istr[ipos+2]){
623 	    ipos += 2;
624 	} else if (istr[ipos+2] != sep) {
625 	    return -(EINVAL + 1);
626 	} else {
627 	    ipos += 3;
628 	}
629     }
630     return len;
631 }
632 
pxechn_parse_opttype(char istr[],int optnum)633 int pxechn_parse_opttype(char istr[], int optnum)
634 {
635     char *pos;
636     int tlen, type, tmask;
637 
638     if (!istr)
639 	return -1;
640     pos = strchr(istr, '=');
641     if (!pos)
642 	return -2;
643     if (istr[0] != '.') {
644 	if (!pxechn_optnum_ok(optnum))
645 	    return -3;
646 	return -3;	/* do lookup here */
647     } else {
648 	tlen = pos - istr - 1;
649 	if ((tlen < 1) || (tlen > 4))
650 	    return -4;
651 	tmask = 0xFFFFFFFF >> (8 * (4 - tlen));
652 	type = (*(int*)(istr + 1)) & tmask;
653     }
654     return type;
655 }
656 
pxechn_parse_setopt(struct dhcp_option opts[],struct dhcp_option * iopt,char istr[])657 int pxechn_parse_setopt(struct dhcp_option opts[], struct dhcp_option *iopt,
658 			char istr[])
659 {
660     int rv = 0, optnum, opttype;
661     char *cpos = NULL, *pos;
662 
663     if (!opts || !iopt || !(iopt->data))
664 	return -1;
665     if (!istr || !istr[0])
666 	return -2;
667     // -EINVAL;
668     optnum = strtoul(istr, &cpos, 0);
669     if (!pxechn_optnum_ok(optnum))
670 	return -3;
671     pos = strchr(cpos, '=');
672     if (!pos)
673 	return -4;
674     opttype = pxechn_parse_opttype(cpos, optnum);
675     pos++;
676     switch(opttype) {
677     case 'b':
678 	iopt->len = pxechn_parse_int(iopt->data, pos, 1);
679 	break;
680     case 'l':
681 	iopt->len = pxechn_parse_int(iopt->data, pos, 4);
682 	break;
683     case 'q':
684 	iopt->len = pxechn_parse_int(iopt->data, pos, 8);
685 	break;
686     case 's':
687     case STRASINT_str:
688 	iopt->len = strlen(pos);
689 	if (iopt->len > DHCP_OPT_LEN_MAX)
690 	    iopt->len = DHCP_OPT_LEN_MAX;
691 	memcpy(iopt->data, pos, iopt->len);
692 	dprintf_pc_so_s("s.len=%d\trv=%d\n", iopt->len, rv);
693 	break;
694     case 'w':
695 	iopt->len = pxechn_parse_int(iopt->data, pos, 2);
696 	break;
697     case 'x':
698 	iopt->len = pxechn_parse_hex_sep(iopt->data, pos, ':');
699 	break;
700     default:
701 	return -6;
702 	break;
703     }
704     if (pxechn_optlen_ok(iopt->len)) {
705 	rv = pxechn_setopt(&(opts[optnum]), (void *)(iopt->data), iopt->len);
706     }
707     if((opttype == 's') || (opttype == STRASINT_str))
708 	dprintf_pc_so_s("rv=%d\n", rv);
709     return rv;
710 }
711 
pxechn_parse_force(const char istr[])712 int pxechn_parse_force(const char istr[])
713 {
714     uint32_t rv = 0;
715     char *pos;
716     int terr = errno;
717 
718     errno = 0;
719     rv = strtoul(istr, &pos, 0);
720     if ((istr == pos ) || ((rv == ULONG_MAX) && (errno)))
721 	rv = 0;
722     errno = terr;
723     return rv;
724 }
725 
pxechn_uuid_set(struct pxelinux_opt * pxe)726 int pxechn_uuid_set(struct pxelinux_opt *pxe)
727 {
728     int ret = 0;
729 
730     if (!pxe->p_unpacked[0])
731 	ret = dhcp_unpack_packet((pxe_bootp_t *)(pxe->p[0].data),
732 				 pxe->p[0].len, pxe->opts[0]);
733     if (ret) {
734 	error("Could not unpack packet\n");
735 	return -ret;	/* dhcp_unpack_packet always returns positive errors */
736     }
737 
738     if (pxe->opts[0][97].len >= 0 )
739 	pxechn_setopt(&(pxe->opts[2][97]), pxe->opts[0][97].data, pxe->opts[0][97].len);
740 	return 1;
741     return 0;
742 }
743 
pxechn_parse_args(int argc,char * argv[],struct pxelinux_opt * pxe,struct dhcp_option opts[])744 int pxechn_parse_args(int argc, char *argv[], struct pxelinux_opt *pxe,
745 			 struct dhcp_option opts[])
746 {
747     int arg, optnum, rv = 0;
748     char *p = NULL;
749     const char optstr[] = "c:f:g:o:p:St:uwW";
750     struct dhcp_option iopt;
751 
752     if (pxe->p[5].data)
753 	pxe->fip = ( (pxe_bootp_t *)(pxe->p[5].data) )->sip;
754     else
755 	pxe->fip = 0;
756     /* Fill */
757     pxe->fn = argv[0];
758     pxechn_parse_fn(pxe->fn, &(pxe->fip), pxe->host, &(pxe->fp));
759     pxechn_setopt_str(&(opts[67]), pxe->fp);
760     pxechn_setopt_str(&(opts[66]), pxe->host);
761     iopt.data = malloc(DHCP_OPT_LEN_MAX);
762     iopt.len = 0;
763     while ((rv >= 0) && (arg = getopt(argc, argv, optstr)) >= 0) {
764 	dprintf_pc_pa("  Got arg '%c'/'%c' addr %08X val %s\n", arg == '?' ? optopt : arg, arg, (unsigned int)optarg, optarg ? optarg : "");
765 	switch(arg) {
766 	case 'c':	/* config */
767 	    pxechn_setopt_str(&(opts[209]), optarg);
768 	    break;
769 	case 'f':	/* force */
770 	    pxe->force = pxechn_parse_force(optarg);
771 	    break;
772 	case 'g':	/* gateway/DHCP relay */
773 	    pxe->gip = pxe_dns(optarg);
774 	    break;
775 	case 'n':	/* native */
776 	    break;
777 	case 'o':	/* option */
778 	    rv = pxechn_parse_setopt(opts, &iopt, optarg);
779 	    break;
780 	case 'p':	/* prefix */
781 	    pxechn_setopt_str(&(opts[210]), optarg);
782 	    break;
783 	case 'S':	/* sip from sName */
784 	    pxe->sip = 1;
785 	    break;
786 	case 't':	/* timeout */
787 	    optnum = strtoul(optarg, &p, 0);
788 	    if (p != optarg) {
789 		optnum = htonl(optnum);
790 		pxechn_setopt(&(opts[211]), (void *)(&optnum), 4);
791 	    } else {
792 		rv = -3;
793 	    }
794 	    break;
795 	case 'u':	/* UUID: copy option 97 from packet 1 if present */
796 	    pxechn_uuid_set(pxe);
797 	    break;
798 	case 'w':	/* wait */
799 	    pxe->wait = 1;
800 	    break;
801 	case 'W':	/* WDS */
802 	    pxe->wds = 1;
803 	    break;
804 	case '?':
805 	    rv = -'?';
806 	default:
807 	    break;
808 	}
809 	if (rv >= 0)	/* Clear it since getopt() doesn't guarentee it */
810 	    optarg = NULL;
811     }
812     if (iopt.data)
813 	pxechn_opt_free(&iopt);
814 /* FIXME: consider reordering the application of parsed command line options
815        such that the new nbp may be at the end */
816     if (rv >= 0) {
817 	rv = 0;
818     } else if (arg != '?') {
819 	printf("Invalid argument for -%c: %s\n", arg, optarg);
820     }
821     dprintf("pxechn_parse_args rv=%d\n", rv);
822     return rv;
823 }
824 
pxechn_args(int argc,char * argv[],struct pxelinux_opt * pxe)825 int pxechn_args(int argc, char *argv[], struct pxelinux_opt *pxe)
826 {
827     pxe_bootp_t *bootp0, *bootp1;
828     int ret = 0;
829     struct dhcp_option *opts;
830     char *str;
831 
832     opts = pxe->opts[2];
833     /* Start filling packet #1 */
834     bootp0 = (pxe_bootp_t *)(pxe->p[2].data);
835     bootp1 = (pxe_bootp_t *)(pxe->p[5].data);
836 
837     ret = dhcp_unpack_packet(bootp0, pxe->p[2].len, opts);
838     if (ret) {
839 	error("Could not unpack packet\n");
840 	return -ret;
841     }
842     pxe->p_unpacked[2] = 1;
843     pxe->gip = bootp1->gip;
844 
845     ret = pxechn_parse_args(argc, argv, pxe, opts);
846     if (ret)
847 	return ret;
848     if (pxe->sip > 0xFFFFFF) {	/* a real IPv4 address */
849 	bootp1->sip = pxe->sip;
850     } else if ((pxe->sip == 1)
851 		&& (opts[66].len > 0)){
852 	/* unterminated? */
853 	if (strnlen(opts[66].data, opts[66].len) == (size_t)opts[66].len) {
854 	    str = malloc(opts[66].len + 1);
855 	    if (str) {
856 		memcpy(str, opts[66].data, opts[66].len);
857 		str[opts[66].len] = 0;
858 	    }
859 	} else {
860 	    str = opts[66].data;
861 	}
862 	if (str) {
863 	    bootp1->sip = pxe_dns(str);
864 	    if (str != opts[66].data)
865 		free(str);
866 	} else {
867 	    bootp1->sip = pxe->fip;
868 	}
869     } else {
870 	bootp1->sip = pxe->fip;
871     }
872     bootp1->gip = pxe->gip;
873 
874     ret = dhcp_pack_packet(bootp1, (size_t *)&(pxe->p[5].len), opts);
875     if (ret) {
876 	error("Could not pack packet\n");
877 	return -ret;	/* dhcp_pack_packet always returns positive errors */
878     }
879     return ret;
880 }
881 
882 /* dhcp_pkt2pxe: Copy packet to PXE's BC data for a ptype packet
883  *	Input:
884  *	p	Packet data to copy
885  *	len	length of data to copy
886  *	ptype	Packet type to overwrite
887  */
dhcp_pkt2pxe(pxe_bootp_t * p,size_t len,int ptype)888 int dhcp_pkt2pxe(pxe_bootp_t *p, size_t len, int ptype)
889 {
890     t_PXENV_GET_CACHED_INFO *ci;
891     void *cp;
892     int rv = -1;
893 
894     if (!(ci = lzalloc(sizeof(t_PXENV_GET_CACHED_INFO)))){
895 	dprintf("Unable to lzalloc() for PXE call structure\n");
896 	rv = 1;
897 	goto ret;
898     }
899     ci->Status = PXENV_STATUS_FAILURE;
900     ci->PacketType = ptype;
901     pxe_call(PXENV_GET_CACHED_INFO, ci);
902 
903     if (ci->Status != PXENV_STATUS_SUCCESS) {
904 	dprintf("PXE Get Cached Info failed: %d\n", ci->Status);
905 	rv = 2;
906 	goto ret;
907     }
908 
909     cp = MK_PTR(ci->Buffer.seg, ci->Buffer.offs);
910     if (!(memcpy(cp, p, len))) {
911 	dprintf("Failed to copy packet\n");
912 	rv = 3;
913 	goto ret;
914     }
915 ret:
916     lfree(ci);
917    return rv;
918 }
919 
pxechn_mergeopt(struct pxelinux_opt * pxe,int d,int s)920 int pxechn_mergeopt(struct pxelinux_opt *pxe, int d, int s)
921 {
922     int ret = 0, i;
923 
924     if ((d >= PXECHN_NUM_PKT_TYPE) || (s >= PXECHN_NUM_PKT_TYPE)
925 	    || (d < 0) || (s < 0)) {
926 	return -2;
927     }
928     if (!pxe->p_unpacked[s])
929 	ret = dhcp_unpack_packet(pxe->p[s].data, pxe->p[s].len, pxe->opts[s]);
930     if (ret) {
931 	error("Could not unpack packet for merge\n");
932 	printf("Error %d (%d)\n", ret, EINVAL);
933 	if (ret == EINVAL) {
934 	    if (pxe->p[s].len < 240)
935 		printf("Packet %d is too short: %d (240)\n", s, pxe->p[s].len);
936 	    else if (((const struct dhcp_packet *)(pxe->p[s].data))->magic != htonl(DHCP_VENDOR_MAGIC))
937 		printf("Packet %d has no magic\n", s);
938 	    else
939 		error("Unknown EINVAL error\n");
940 	} else {
941 	    error("Unknown error\n");
942 	}
943 	return -ret;
944     }
945     for (i = 0; i < NUM_DHCP_OPTS; i++) {
946 	if (pxe->opts[d][i].len <= -1) {
947 	    if (pxe->opts[s][i].len >= 0)
948 		pxechn_setopt(&(pxe->opts[d][i]), pxe->opts[s][i].data, pxe->opts[s][i].len);
949 	}
950     }
951     return 0;
952 }
953 
954 /* pxechn: Chainload to new PXE file ourselves
955  *	Input:
956  *	argc	Count of arguments passed
957  *	argv	Values of arguments passed
958  *	Returns	0 on success (which should never happen)
959  *		1 on loadfile() error
960  *		2 if DHCP Option 52 (Option Overload) used file field
961  *		-1 on usage error
962  */
pxechn(int argc,char * argv[])963 int pxechn(int argc, char *argv[])
964 {
965     struct pxelinux_opt pxe;
966     pxe_bootp_t* p[(2 * PXECHN_NUM_PKT_TYPE)];
967     int rv = 0;
968     int i;
969     struct data_area file;
970     struct syslinux_rm_regs regs;
971 
972     pxechn_init(&pxe);
973     for (i = 0; i < (2 * PXECHN_NUM_PKT_TYPE); i++) {
974 	p[i] = (pxe_bootp_t *)(pxe.p[i].data);
975     }
976 
977     /* Parse arguments and patch packet 1 */
978     rv = pxechn_args(argc, argv, &pxe);
979     dpressanykey(INT_MAX);
980     if (rv)
981 	goto ret;
982     pxe_set_regs(&regs);
983     /* Load the file late; it's the most time-expensive operation */
984     printf("%s: Attempting to load '%s': ", app_name_str, pxe.fn);
985     if (loadfile(pxe.fn, &file.data, &file.size)) {
986 	pxe_error(errno, NULL, NULL);
987 	rv = -2;
988 	goto ret;
989     }
990     puts("loaded.");
991     /* we'll be shuffling to the standard location of 7C00h */
992     file.base = 0x7C00;
993     if ((pxe.wds) ||
994 	    ((pxe.force) && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0))) {
995 	printf("Forcing behavior %08X\n", pxe.force);
996 	// P2 is the same as P3 if no PXE server present.
997 	if ((pxe.wds) ||
998 		(pxe.force & PXECHN_FORCE_PKT2)) {
999 	    pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_DHCP_ACK);
1000 	    rv = pxechn_mergeopt(&pxe, 2, 1);
1001 	    if (rv) {
1002 		dprintf("Merge Option returned %d\n", rv);
1003 	    }
1004 	    rv = dhcp_pack_packet(p[5], (size_t *)&(pxe.p[5].len), pxe.opts[2]);
1005 	    rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK);
1006 	}
1007 	if (pxe.force & PXECHN_FORCE_PKT1) {
1008 	    puts("Unimplemented force option utilized");
1009 	}
1010     }
1011     rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_CACHED_REPLY);
1012     dprint_pxe_bootp_t(p[5], pxe.p[5].len);
1013     if ((pxe.wds) ||
1014 	    ((pxe.force) && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0))) {
1015 	// printf("Forcing behavior %08X\n", pxe.force);
1016 	// P2 is the same as P3 if no PXE server present.
1017 	if ((pxe.wds) ||
1018 		(pxe.force & PXECHN_FORCE_PKT2)) {
1019 	    rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK);
1020 	}
1021     } else if (pxe.force) {
1022 	printf("FORCE: bad argument %08X\n", pxe.force);
1023     }
1024     printf("\n...Ready to boot:\n");
1025     if (pxe.wait) {
1026 	pressanykey(INT_MAX);
1027     } else {
1028 	dpressanykey(INT_MAX);
1029     }
1030     if (true) {
1031 	puts("  Attempting to boot...");
1032 	do_boot(&file, 1, &regs);
1033     }
1034     /* If failed, copy backup back in and abort */
1035     dhcp_pkt2pxe(p[2], pxe.p[2].len, PXENV_PACKET_TYPE_CACHED_REPLY);
1036     if (pxe.force && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0)) {
1037 	if (pxe.force & PXECHN_FORCE_PKT2) {
1038 	    rv = dhcp_pkt2pxe(p[1], pxe.p[1].len, PXENV_PACKET_TYPE_DHCP_ACK);
1039 	}
1040     }
1041 ret:
1042     return rv;
1043 }
1044 
1045 /* pxe_restart: Restart the PXE environment with a new PXE file
1046  *	Input:
1047  *	ifn	Name of file to chainload to in a format PXELINUX understands
1048  *		This must strictly be TFTP or relative file
1049  */
pxe_restart(char * ifn)1050 int pxe_restart(char *ifn)
1051 {
1052     int rv = 0;
1053     struct pxelinux_opt pxe;
1054     t_PXENV_RESTART_TFTP *pxep;	/* PXENV callback Parameter */
1055 
1056     pxe.fn = ifn;
1057     pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_CACHED_REPLY);
1058     if (pxe.p[5].data)
1059 	pxe.fip = ( (pxe_bootp_t *)(pxe.p[5].data) )->sip;
1060     else
1061 	pxe.fip = 0;
1062     rv = pxechn_parse_fn(pxe.fn, &(pxe.fip), pxe.host, &(pxe.fp));
1063     if ((rv > 2) || (rv < 0)) {
1064 	printf("%s: ERROR: Unparsable filename argument: '%s'\n\n", app_name_str, pxe.fn);
1065 	goto ret;
1066     }
1067     printf("  Attempting to boot '%s'...\n\n", pxe.fn);
1068     if (!(pxep = lzalloc(sizeof(t_PXENV_RESTART_TFTP)))){
1069 	dprintf("Unable to lzalloc() for PXE call structure\n");
1070 	goto ret;
1071     }
1072     pxep->Status = PXENV_STATUS_SUCCESS;	/* PXENV_STATUS_FAILURE */
1073     strcpy((char *)pxep->FileName, ifn);
1074     pxep->BufferSize = 0x8000;
1075     pxep->Buffer = (void *)0x7c00;
1076     pxep->ServerIPAddress = pxe.fip;
1077     dprintf("FN='%s'  %08X %08X %08X %08X\n\n", (char *)pxep->FileName,
1078 	pxep->ServerIPAddress, (unsigned int)pxep,
1079 	pxep->BufferSize, (unsigned int)pxep->Buffer);
1080     dprintf("PXENV_RESTART_TFTP status %d\n", pxep->Status);
1081 
1082     pxe_call(PXENV_RESTART_TFTP, pxep);
1083 
1084     printf("PXENV_RESTART_TFTP returned %d\n", pxep->Status);
1085     lfree(pxep);
1086 
1087 ret:
1088     return rv;
1089 }
1090 
1091 /* pxechn_gpxe: Use gPXE to chainload a new NBP
1092  *	Input:
1093  *	argc	Count of arguments passed
1094  *	argv	Values of arguments passed
1095  *	Returns	0 on success (which should never happen)
1096  *		1 on loadfile() error
1097  *		-1 on usage error
1098  */
1099 //FIXME:Implement
pxechn_gpxe(int argc,char * argv[])1100 int pxechn_gpxe(int argc, char *argv[])
1101 {
1102     int rv = 0;
1103     struct pxelinux_opt pxe;
1104 
1105     if (argc) {
1106 	printf("%s\n", argv[0]);
1107 	pxechn_args(argc, argv, &pxe);
1108     }
1109     return rv;
1110 }
1111 
main(int argc,char * argv[])1112 int main(int argc, char *argv[])
1113 {
1114     int rv= -1;
1115     int err;
1116     const struct syslinux_version *sv;
1117 
1118     /* Initialization */
1119     err = errno;
1120     console_ansi_raw();	/* sets errno = 9 (EBADF) */
1121     /* printf("%d %d\n", err, errno); */
1122     errno = err;
1123     sv = syslinux_version();
1124     if (sv->filesystem != SYSLINUX_FS_PXELINUX) {
1125 	printf("%s: May only run in PXELINUX\n", app_name_str);
1126 	argc = 1;	/* prevents further processing to boot */
1127     }
1128     if (argc == 2) {
1129 	if ((strcasecmp(argv[1], "-h") == 0) || ((strcmp(argv[1], "-?") == 0))
1130 		|| (strcasecmp(argv[1], "--help") == 0)) {
1131 	    argc = 1;
1132 	} else {
1133 	    rv = pxechn(argc - 1, &argv[1]);
1134 	}
1135     } else if (argc >= 3) {
1136 	if ((strcmp(argv[1], "-r") == 0)) {
1137 	    if (argc == 3)
1138 		rv = pxe_restart(argv[2]);
1139 	} else {
1140 	    rv = pxechn(argc - 1, &argv[1]);
1141 	}
1142     }
1143     if (rv <= -1 ) {
1144 	usage();
1145 	rv = 1;
1146     }
1147     return rv;
1148 }
1149