• Home
  • History
  • Annotate
  • 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