57fc6506a1ae79e10d62b267b56305c5c330a59d
[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(request->packet,
63                                     vp->vp_octets, vp->length, &head) < 0) || (!head)) {
64                 RWDEBUG("DHCP option decoding failed: %s", fr_strerror());
65                 *out = '\0';
66                 return -1;
67         }
68
69
70         for (vp = fr_cursor_init(&cursor, &head);
71              vp;
72              vp = fr_cursor_next(&cursor)) {
73                 decoded++;
74         }
75
76         pairmove(request->packet, &(request->packet->vps), &head);
77
78         /* Free any unmoved pairs */
79         pairfree(&head);
80
81         snprintf(out, freespace, "%i", decoded);
82
83         return strlen(out);
84 }
85
86 static ssize_t dhcp_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
87 {
88         vp_cursor_t cursor;
89         VALUE_PAIR *vp;
90         uint8_t binbuf[255];
91         ssize_t len;
92
93         while (isspace((int) *fmt)) fmt++;
94
95         if ((radius_copy_vp(&vp, request, fmt) < 0) || !vp) {
96                  *out = '\0';
97                  return 0;
98         }
99         fr_cursor_init(&cursor, &vp);
100
101         len = fr_dhcp_encode_option(binbuf, sizeof(binbuf), request, &cursor);
102         talloc_free(vp);
103         if (len <= 0) {
104                 REDEBUG("DHCP option encoding failed: %s", fr_strerror());
105
106                 return -1;
107         }
108
109         if ((size_t)((len * 2) + 1) > freespace) {
110                 REDEBUG("DHCP option encoding failed: Output buffer exhausted, needed %zd bytes, have %zd bytes",
111                         (len * 2) + 1, freespace);
112
113                 return -1;
114         }
115
116         return fr_bin2hex(out, binbuf, len);
117 }
118
119 /*
120  *      Only free memory we allocated.  The strings allocated via
121  *      cf_section_parse() do not need to be freed.
122  */
123 static int mod_detach(void *instance)
124 {
125         xlat_unregister("dhcp_options", dhcp_options_xlat, instance);
126         xlat_unregister("dhcp", dhcp_xlat, instance);
127         return 0;
128 }
129
130
131 /*
132  *      Instantiate the module.
133  */
134 static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
135 {
136         rlm_dhcp_t *inst = instance;
137
138         xlat_register("dhcp_options", dhcp_options_xlat, NULL, inst);
139         xlat_register("dhcp", dhcp_xlat, NULL, inst);
140
141         return 0;
142 }
143
144
145 /*
146  *      The module name should be the only globally exported symbol.
147  *      That is, everything else should be 'static'.
148  *
149  *      If the module needs to temporarily modify it's instantiation
150  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
151  *      The server will then take care of ensuring that the module
152  *      is single-threaded.
153  */
154 module_t rlm_dhcp = {
155         RLM_MODULE_INIT,
156         "dhcp",
157         0,                              /* type */
158         sizeof(rlm_dhcp_t),
159         NULL,                           /* CONF_PARSER */
160         mod_instantiate,                /* instantiation */
161         mod_detach,                     /* detach */
162         {
163                 NULL,                   /* authentication */
164                 NULL,                   /* authorization */
165                 NULL,                   /* preaccounting */
166                 NULL,                   /* accounting */
167                 NULL,                   /* checksimul */
168                 NULL,                   /* pre-proxy */
169                 NULL,                   /* post-proxy */
170                 NULL,                   /* post-auth */
171         },
172 };