/* ************************************************************************** * * Boot-ROM-Code to load an operating system across a TCP/IP network. * * Module: getvend.c * Purpose: Get information out of BOOTP/DHCP answer * Entries: get_vend * ************************************************************************** * * Copyright (C) 1998-2003 Gero Kuhlmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: getvend.c,v 1.4 2003/01/25 23:29:41 gkminix Exp $ */ #include #include #include #include #include "bootp.h" /* ************************************************************************** * * Variables local to this module */ static int lastbuf = -1; /* buffer with last requested ID */ static int lastid; /* last request ID */ static int overload; /* overload flags */ static unsigned char *pxeptr; /* pointer to PXE vendor record */ static unsigned char *lastptr; /* pointer to last requested ID */ /* ************************************************************************** * * Find a tag in the BOOTP/DHCP record. * */ static unsigned char *find_tag(id, startp, endp) int id; unsigned char *startp; unsigned char *endp; { register unsigned char *cp = startp; while (*cp != VEND_END && cp < endp) { if (*cp != VEND_NOP) { if (*cp == id) break; cp += *(cp + 1) + 1; } cp++; } return(*cp == id ? cp + 1 : NULL); } /* ************************************************************************** * * Find a tag either in the vendor information area or the boot filename * or server name fields. */ static unsigned char *search_tag(id, lastp) int id; unsigned char *lastp; { register unsigned char *cp; unsigned char *startp, *endp; unsigned char *retp = NULL; /* Setup start and end pointers */ startp = bootp_bufs[cur_bootp_buf]->bp_vend + VM_SIZE; endp = (unsigned char *)(bootp_bufs[cur_bootp_buf]) + bootp_sizes[cur_bootp_buf]; /* Search for the tag in the vendor extension area */ if (lastp == NULL) retp = find_tag(id, startp, endp); else if (lastp >= startp && lastp < endp) retp = find_tag(id, lastp, endp); /* Only search overload areas for valid tags, not NOP, END etc. */ if (id != VEND_NOP && id != VEND_END && id != VEND_OVERLOAD) { /* Search for the tag in the filename area if buffer is overloaded */ if (retp == NULL && (overload & VEND_OVR_FILE)) { cp = bootp_bufs[cur_bootp_buf]->bp_file; if (lastp == NULL) retp = find_tag(id, cp, cp + BOOTP_FILE_SIZE); else if (lastp >= cp && lastp < (cp + BOOTP_FILE_SIZE)) retp = find_tag(id, lastp, cp + BOOTP_FILE_SIZE); } /* Search for the tag in the server name area if buffer is overloaded */ if (retp == NULL && (overload & VEND_OVR_SNAME)) { cp = bootp_bufs[cur_bootp_buf]->bp_sname; if (lastp == NULL) retp = find_tag(id, cp, cp + BOOTP_SNAME_SIZE); else if (lastp >= cp && lastp < (cp + BOOTP_SNAME_SIZE)) retp = find_tag(id, lastp, cp + BOOTP_SNAME_SIZE); } } return(retp); } /* ************************************************************************** * * Return the overload flag of a BOOTP/DHCP buffer */ static void check_overload() { register unsigned char *cp; overload = 0; cp = search_tag(VEND_OVERLOAD, NULL); if (cp != NULL) { cp++; overload = *cp; } } /* ************************************************************************** * * Check if we have PXE vendor extensions */ static void check_pxe() { register unsigned char *cp; pxeptr = NULL; cp = search_tag(VEND_CLASS, NULL); if (cp != NULL && *cp == VEND_PXE_STRLEN) { cp++; if (!memcmp(cp, VEND_PXE_STRING, VEND_PXE_STRLEN)) pxeptr = search_tag(VEND_VENDOR, NULL); } } /* ************************************************************************** * * Return information from the vendor area of a BOOTP/DHCP record. Note * that this will work only with RFC1048-compliant messages. * It will return a pointer to the length byte of the tag. * */ unsigned char *get_vend(id) int id; { register unsigned char *cp; unsigned char *endp; unsigned char *retp = NULL; cp = (unsigned char *)(bootp_bufs[cur_bootp_buf]); if (cp != NULL) { cp = bootp_bufs[cur_bootp_buf]->bp_vend; if (*cp) { /* Check if we are accessing the same buffer as before */ if (lastbuf != cur_bootp_buf) { lastbuf = cur_bootp_buf; lastptr = NULL; check_overload(); check_pxe(); } else { /* Determine start address for new search */ if (lastid != id) lastptr = NULL; else if (lastptr != NULL) lastptr += *lastptr + 1; } /* Now actually search for the tag, NOP just recycles */ if (id == VEND_NOP) retp = NULL; else if (id < VEND_PXE_BASE) retp = search_tag(id, lastptr); else if (pxeptr != NULL) { cp = pxeptr; endp = cp + *cp + 1; cp++; if (lastptr == NULL) retp = find_tag(id & VEND_PXE_MASK, cp, endp); else if (lastptr >= cp && lastptr < endp) retp = find_tag(id & VEND_PXE_MASK, lastptr, endp); } } } lastid = id; lastptr = retp; return(retp); }