Add support for Vendor Specific Suboptions (RFC 4243)
[freeradius.git] / src / modules / proto_dhcp / rlm_dhcp.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15
16 /**
17  * $Id$
18  * @file rlm_dhcp.c
19  * @brief Will contain dhcp listener code.
20  *
21  * @copyright 2012  The FreeRADIUS server project
22  */
23 RCSID("$Id$")
24
25 #include <freeradius-devel/libradius.h>
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include <freeradius-devel/dhcp.h>
30
31 #include <ctype.h>
32
33 /*
34  *      Define a structure for our module configuration.
35  *
36  *      These variables do not need to be in a structure, but it's
37  *      a lot cleaner to do so, and a pointer to the structure can
38  *      be used as the instance handle.
39  */
40 typedef struct rlm_dhcp_t {
41         int nothing;
42 } rlm_dhcp_t;
43
44
45 /*
46  *      Allow single attribute values to be retrieved from the dhcp.
47  */
48 static ssize_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request,
49                                  char const *fmt, char *out, size_t freespace)
50 {
51         vp_cursor_t cursor;
52         VALUE_PAIR *vp, *head = NULL;
53         int decoded = 0;
54
55         while (isspace((int) *fmt)) fmt++;
56
57         if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
58                  *out = '\0';
59                  return 0;
60         }
61
62         if ((fr_dhcp_decode_options(&head, request->packet, vp->vp_octets, vp->length) < 0) || (!head)) {
63                 RWDEBUG("DHCP option decoding failed: %s", fr_strerror());
64                 *out = '\0';
65                 return -1;
66         }
67
68
69         for (vp = fr_cursor_init(&cursor, &head);
70              vp;
71              vp = fr_cursor_next(&cursor)) {
72                 decoded++;
73         }
74
75         pairmove(request->packet, &(request->packet->vps), &head);
76
77         /* Free any unmoved pairs */
78         pairfree(&head);
79
80         snprintf(out, freespace, "%i", decoded);
81
82         return strlen(out);
83 }
84
85 static ssize_t dhcp_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
86 {
87         vp_cursor_t cursor;
88         VALUE_PAIR *vp;
89         uint8_t binbuf[255];
90         ssize_t len;
91
92         while (isspace((int) *fmt)) fmt++;
93
94         if ((radius_copy_vp(&vp, request, fmt) < 0) || !vp) {
95                  *out = '\0';
96                  return 0;
97         }
98         fr_cursor_init(&cursor, &vp);
99
100         len = fr_dhcp_encode_option(binbuf, sizeof(binbuf), request, &cursor);
101         talloc_free(vp);
102         if (len <= 0) {
103                 REDEBUG("DHCP option encoding failed: %s", fr_strerror());
104
105                 return -1;
106         }
107
108         if ((size_t)((len * 2) + 1) > freespace) {
109                 REDEBUG("DHCP option encoding failed: Output buffer exhausted, needed %zd bytes, have %zd bytes",
110                         (len * 2) + 1, freespace);
111
112                 return -1;
113         }
114
115         return fr_bin2hex(out, binbuf, len);
116 }
117
118 /*
119  *      Only free memory we allocated.  The strings allocated via
120  *      cf_section_parse() do not need to be freed.
121  */
122 static int mod_detach(void *instance)
123 {
124         xlat_unregister("dhcp_options", dhcp_options_xlat, instance);
125         xlat_unregister("dhcp", dhcp_xlat, instance);
126         return 0;
127 }
128
129
130 /*
131  *      Instantiate the module.
132  */
133 static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
134 {
135         rlm_dhcp_t *inst = instance;
136
137         xlat_register("dhcp_options", dhcp_options_xlat, NULL, inst);
138         xlat_register("dhcp", dhcp_xlat, NULL, inst);
139
140         return 0;
141 }
142
143
144 /*
145  *      The module name should be the only globally exported symbol.
146  *      That is, everything else should be 'static'.
147  *
148  *      If the module needs to temporarily modify it's instantiation
149  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
150  *      The server will then take care of ensuring that the module
151  *      is single-threaded.
152  */
153 module_t rlm_dhcp = {
154         RLM_MODULE_INIT,
155         "dhcp",
156         0,                              /* type */
157         sizeof(rlm_dhcp_t),
158         NULL,                           /* CONF_PARSER */
159         mod_instantiate,                /* instantiation */
160         mod_detach,                     /* detach */
161         {
162                 NULL,                   /* authentication */
163                 NULL,                   /* authorization */
164                 NULL,                   /* preaccounting */
165                 NULL,                   /* accounting */
166                 NULL,                   /* checksimul */
167                 NULL,                   /* pre-proxy */
168                 NULL,                   /* post-proxy */
169                 NULL,                   /* post-auth */
170         },
171 };