src/openvpn/dhcp.c
03731db3
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
03731db3
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
  *  as published by the Free Software Foundation.
  *
  *  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.
  *
caa54ac3
  *  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.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
03731db3
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
03731db3
 #include "syshead.h"
 
 #include "dhcp.h"
 #include "socket.h"
 #include "error.h"
 
 #include "memdbg.h"
 
 static int
81d882d5
 get_dhcp_message_type(const struct dhcp *dhcp, const int optlen)
03731db3
 {
81d882d5
     const uint8_t *p = (uint8_t *) (dhcp + 1);
     int i;
03731db3
 
81d882d5
     for (i = 0; i < optlen; ++i)
03731db3
     {
81d882d5
         const uint8_t type = p[i];
         const int room = optlen - i;
         if (type == DHCP_END)         /* didn't find what we were looking for */
         {
             return -1;
         }
         else if (type == DHCP_PAD)    /* no-operation */
         {
         }
         else if (type == DHCP_MSG_TYPE) /* what we are looking for */
         {
             if (room >= 3)
             {
                 if (p[i+1] == 1)      /* option length should be 1 */
                 {
                     return p[i+2];    /* return message type */
                 }
             }
             return -1;
         }
         else                          /* some other option */
         {
             if (room >= 2)
             {
                 const int len = p[i+1]; /* get option length */
                 i += (len + 1);       /* advance to next option */
             }
         }
03731db3
     }
81d882d5
     return -1;
03731db3
 }
 
 static in_addr_t
81d882d5
 do_extract(struct dhcp *dhcp, int optlen)
03731db3
 {
81d882d5
     uint8_t *p = (uint8_t *) (dhcp + 1);
     int i;
     in_addr_t ret = 0;
03731db3
 
81d882d5
     for (i = 0; i < optlen; )
03731db3
     {
81d882d5
         const uint8_t type = p[i];
         const int room = optlen - i;
         if (type == DHCP_END)
         {
             break;
         }
         else if (type == DHCP_PAD)
         {
             ++i;
         }
         else if (type == DHCP_ROUTER)
         {
             if (room >= 2)
             {
                 const int len = p[i+1]; /* get option length */
                 if (len <= (room-2))
                 {
                     /* get router IP address */
                     if (!ret && len >= 4 && (len & 3) == 0)
                     {
                         memcpy(&ret, p+i+2, 4);
                         ret = ntohl(ret);
                     }
                     {
                         /* delete the router option */
                         uint8_t *dest = p + i;
                         const int owlen = len + 2;        /* len of data to overwrite */
                         uint8_t *src = dest + owlen;
                         uint8_t *end = p + optlen;
                         const int movlen = end - src;
                         if (movlen > 0)
                         {
                             memmove(dest, src, movlen);   /* overwrite router option */
                         }
                         memset(end - owlen, DHCP_PAD, owlen); /* pad tail */
                     }
                 }
                 else
                 {
                     break;
                 }
             }
             else
             {
                 break;
             }
         }
         else                            /* some other option */
         {
             if (room >= 2)
             {
                 const int len = p[i+1]; /* get option length */
                 i += (len + 2);         /* advance to next option */
             }
             else
             {
                 break;
             }
         }
03731db3
     }
81d882d5
     return ret;
03731db3
 }
 
 in_addr_t
81d882d5
 dhcp_extract_router_msg(struct buffer *ipbuf)
03731db3
 {
81d882d5
     struct dhcp_full *df = (struct dhcp_full *) BPTR(ipbuf);
     const int optlen = BLEN(ipbuf) - (sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_udphdr) + sizeof(struct dhcp));
 
     if (optlen >= 0
         && df->ip.protocol == OPENVPN_IPPROTO_UDP
         && df->udp.source == htons(BOOTPS_PORT)
         && df->udp.dest == htons(BOOTPC_PORT)
         && df->dhcp.op == BOOTREPLY)
03731db3
     {
81d882d5
         const int message_type = get_dhcp_message_type(&df->dhcp, optlen);
         if (message_type == DHCPACK || message_type == DHCPOFFER)
         {
             /* get the router IP address while padding out all DHCP router options */
             const in_addr_t ret = do_extract(&df->dhcp, optlen);
 
             /* recompute the UDP checksum */
             df->udp.check = 0;
e11d2d14
             df->udp.check = htons(ip_checksum(AF_INET, (uint8_t *)&df->udp,
                                               sizeof(struct openvpn_udphdr) + sizeof(struct dhcp) + optlen,
                                               (uint8_t *)&df->ip.saddr, (uint8_t *)&df->ip.daddr,
                                               OPENVPN_IPPROTO_UDP));
81d882d5
 
             /* only return the extracted Router address if DHCPACK */
             if (message_type == DHCPACK)
             {
                 if (ret)
                 {
                     struct gc_arena gc = gc_new();
                     msg(D_ROUTE, "Extracted DHCP router address: %s", print_in_addr_t(ret, 0, &gc));
                     gc_free(&gc);
                 }
 
                 return ret;
             }
         }
03731db3
     }
81d882d5
     return 0;
03731db3
 }