2013-12-10 01:43:11 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright ( C ) 2013 Intel Corporation . All rights reserved .
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
( at your option ) any later version .
systemd 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
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <stdint.h>
# include <string.h>
# include <errno.h>
# include <stdio.h>
# include "dhcp-internal.h"
2014-05-20 13:04:50 +04:00
int dhcp_option_append ( uint8_t options [ ] , size_t size , size_t * offset ,
uint8_t code , size_t optlen , const void * optval ) {
assert ( options ) ;
assert ( offset ) ;
2013-12-10 01:43:11 +04:00
switch ( code ) {
case DHCP_OPTION_PAD :
case DHCP_OPTION_END :
2014-05-20 13:04:50 +04:00
if ( size - * offset < 1 )
2013-12-10 01:43:11 +04:00
return - ENOBUFS ;
2014-05-20 13:04:50 +04:00
options [ * offset ] = code ;
* offset + = 1 ;
2013-12-10 01:43:11 +04:00
break ;
default :
2014-05-20 13:04:50 +04:00
if ( size - * offset < optlen + 2 )
2013-12-10 01:43:11 +04:00
return - ENOBUFS ;
2014-05-20 13:04:50 +04:00
assert ( optval ) ;
2013-12-10 01:43:11 +04:00
2014-05-20 13:04:50 +04:00
options [ * offset ] = code ;
options [ * offset + 1 ] = optlen ;
memcpy ( & options [ * offset + 2 ] , optval , optlen ) ;
2013-12-10 01:43:11 +04:00
2014-05-20 13:04:50 +04:00
* offset + = optlen + 2 ;
2013-12-10 01:43:11 +04:00
break ;
}
return 0 ;
}
static int parse_options ( const uint8_t * buf , size_t buflen , uint8_t * overload ,
uint8_t * message_type , dhcp_option_cb_t cb ,
void * user_data )
{
const uint8_t * code = buf ;
const uint8_t * len ;
while ( buflen > 0 ) {
switch ( * code ) {
case DHCP_OPTION_PAD :
buflen - = 1 ;
code + + ;
break ;
case DHCP_OPTION_END :
return 0 ;
case DHCP_OPTION_MESSAGE_TYPE :
if ( buflen < 3 )
return - ENOBUFS ;
buflen - = 3 ;
len = code + 1 ;
if ( * len ! = 1 )
return - EINVAL ;
if ( message_type )
* message_type = * ( len + 1 ) ;
code + = 3 ;
break ;
case DHCP_OPTION_OVERLOAD :
if ( buflen < 3 )
return - ENOBUFS ;
buflen - = 3 ;
len = code + 1 ;
if ( * len ! = 1 )
return - EINVAL ;
if ( overload )
* overload = * ( len + 1 ) ;
code + = 3 ;
break ;
default :
if ( buflen < 3 )
return - ENOBUFS ;
len = code + 1 ;
if ( buflen < ( size_t ) * len + 2 )
return - EINVAL ;
buflen - = * len + 2 ;
if ( cb )
cb ( * code , * len , len + 1 , user_data ) ;
code + = * len + 2 ;
break ;
}
}
if ( buflen )
return - EINVAL ;
return 0 ;
}
int dhcp_option_parse ( DHCPMessage * message , size_t len ,
dhcp_option_cb_t cb , void * user_data )
{
uint8_t overload = 0 ;
uint8_t message_type = 0 ;
int res ;
if ( ! message )
return - EINVAL ;
2014-04-06 16:05:32 +04:00
if ( len < sizeof ( DHCPMessage ) )
2013-12-10 01:43:11 +04:00
return - EINVAL ;
2014-04-06 16:05:32 +04:00
len - = sizeof ( DHCPMessage ) ;
2013-12-10 01:43:11 +04:00
2014-05-20 13:04:50 +04:00
res = parse_options ( message - > options , len , & overload , & message_type ,
2014-04-06 16:05:32 +04:00
cb , user_data ) ;
2013-12-10 01:43:11 +04:00
if ( res < 0 )
return res ;
if ( overload & DHCP_OVERLOAD_FILE ) {
res = parse_options ( message - > file , sizeof ( message - > file ) ,
NULL , & message_type , cb , user_data ) ;
if ( res < 0 )
return res ;
}
if ( overload & DHCP_OVERLOAD_SNAME ) {
res = parse_options ( message - > sname , sizeof ( message - > sname ) ,
NULL , & message_type , cb , user_data ) ;
if ( res < 0 )
return res ;
}
if ( message_type )
return message_type ;
return - ENOMSG ;
}