/
static.c
231 lines (199 loc) · 8.11 KB
/
static.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/* Copyright © 2012 Brandon L Black <blblack@gmail.com> and Jay Reitz <jreitz@gmail.com>
*
* This file is part of gdnsd.
*
* gdnsd 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 3 of the License, or
* (at your option) any later version.
*
* gdnsd 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 gdnsd. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <config.h>
#include <gdnsd/compiler.h>
#include <gdnsd/alloc.h>
#include <gdnsd/log.h>
#include <gdnsd/vscf.h>
#include "mon.h"
#include "plugapi.h"
#include "plugins.h"
#include <stdbool.h>
#include <inttypes.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
typedef struct {
const char* name;
bool is_addr;
union {
gdnsd_anysin_t addr;
uint8_t* dname;
};
} static_resource_t;
static static_resource_t* resources = NULL;
static unsigned num_resources = 0;
static bool config_res(const char* resname, unsigned resname_len V_UNUSED, vscf_data_t* addr, void* data)
{
unsigned* residx_ptr = data;
if (vscf_get_type(addr) != VSCF_SIMPLE_T)
log_fatal("plugin_static: resource %s: must be an IP address or a domainname in string form", resname);
unsigned res = *residx_ptr;
(*residx_ptr)++;
resources[res].name = xstrdup(resname);
const char* addr_txt = vscf_simple_get_data(addr);
if (gdnsd_anysin_fromstr(addr_txt, 0, &resources[res].addr)) {
// Address-parsing failed, treat as domainname for DYNC
resources[res].is_addr = false;
resources[res].dname = xmalloc(256);
dname_status_t status = vscf_simple_get_as_dname(addr, resources[res].dname);
if (status == DNAME_INVALID)
log_fatal("plugin_static: resource %s: must be an IPv4 address or a domainname in string form", resname);
if (status == DNAME_PARTIAL)
log_fatal("plugin_static: resource %s: '%s' must be fully qualified (end in dot)", resname, addr_txt);
gdnsd_assert(status == DNAME_VALID);
resources[res].dname = dname_trim(resources[res].dname);
} else {
resources[res].is_addr = true;
}
return true;
}
static void plugin_static_load_config(vscf_data_t* config)
{
if (!config)
log_fatal("static plugin requires a 'plugins' configuration stanza");
gdnsd_assert(vscf_get_type(config) == VSCF_HASH_T);
num_resources = vscf_hash_get_len(config);
if (num_resources) {
resources = xmalloc_n(num_resources, sizeof(*resources));
unsigned residx = 0;
vscf_hash_iterate(config, false, config_res, &residx);
gdnsd_dyn_addr_max(1, 1); // static only ever returns a single IP
}
}
static int plugin_static_map_res(const char* resname, const uint8_t* zone_name)
{
if (resname) {
for (unsigned i = 0; i < num_resources; i++) {
if (!strcmp(resname, resources[i].name)) {
if (resources[i].is_addr) {
if (zone_name)
log_warn("plugin_static: resource %s used from zone %s: DYNC configurations which can return IP address results are DEPRECATED and will be removed in a future version!", resname, logf_dname(zone_name));
return (int)i;
}
if (!zone_name)
map_res_err("plugin_static: CNAME resource '%s' cannot be used for a DYNA record", resources[i].name);
uint8_t* dname = resources[i].dname;
if (dname_isinzone(zone_name, dname))
map_res_err("plugin_static: Resource '%s' CNAME value '%s' cannot be used within zone '%s'", resources[i].name, logf_dname(dname), logf_dname(zone_name));
return (int)i;
}
}
map_res_err("plugin_static: Unknown resource '%s'", resname);
}
map_res_err("plugin_static: resource name required");
}
static gdnsd_sttl_t plugin_static_resolve(unsigned resnum V_UNUSED, const client_info_t* cinfo V_UNUSED, dyn_result_t* result)
{
if (resources[resnum].is_addr)
gdnsd_result_add_anysin(result, &resources[resnum].addr);
else
gdnsd_result_add_cname(result, resources[resnum].dname);
return GDNSD_STTL_TTL_MAX;
}
// plugin_static as a monitoring plugin:
typedef struct {
const char* name;
gdnsd_sttl_t static_sttl;
} static_svc_t;
typedef struct {
static_svc_t* svc;
unsigned idx;
} static_mon_t;
static unsigned num_svcs = 0;
static unsigned num_mons = 0;
static static_svc_t** static_svcs = NULL;
static static_mon_t** static_mons = NULL;
static void plugin_static_add_svctype(const char* name, vscf_data_t* svc_cfg, const unsigned interval V_UNUSED, const unsigned timeout V_UNUSED)
{
static_svc_t* this_svc = xmalloc(sizeof(*this_svc));
static_svcs = xrealloc_n(static_svcs, num_svcs + 1, sizeof(*static_svcs));
static_svcs[num_svcs] = this_svc;
num_svcs++;
this_svc->name = xstrdup(name);
this_svc->static_sttl = GDNSD_STTL_TTL_MAX;
vscf_data_t* ttl_data = vscf_hash_get_data_byconstkey(svc_cfg, "ttl", true);
if (ttl_data) {
unsigned long fixed_ttl = 0;
if (!vscf_is_simple(ttl_data) || !vscf_simple_get_as_ulong(ttl_data, &fixed_ttl))
log_fatal("plugin_static: service type '%s': the value of 'ttl' must be a simple integer!", name);
if (fixed_ttl > GDNSD_STTL_TTL_MAX)
log_fatal("plugin_static: service type '%s': the value of 'ttl' must be <= %u", name, GDNSD_STTL_TTL_MAX);
this_svc->static_sttl = fixed_ttl;
}
vscf_data_t* state_data = vscf_hash_get_data_byconstkey(svc_cfg, "state", true);
if (state_data) {
if (!vscf_is_simple(state_data))
log_fatal("plugin_static: service type '%s': the value of 'state' must be 'up' or 'down' as a simple string!", name);
const char* state_txt = vscf_simple_get_data(state_data);
if (!strcasecmp(state_txt, "down"))
this_svc->static_sttl |= GDNSD_STTL_DOWN;
else if (strcasecmp(state_txt, "up"))
log_fatal("plugin_static: service type '%s': the value of 'state' must be 'up' or 'down', not '%s'", name, state_txt);
}
}
static void add_mon_any(const char* svc_name, const unsigned idx)
{
gdnsd_assert(svc_name);
static_svc_t* this_svc = NULL;
for (unsigned i = 0; i < num_svcs; i++) {
if (!strcmp(svc_name, static_svcs[i]->name)) {
this_svc = static_svcs[i];
break;
}
}
gdnsd_assert(this_svc);
static_mon_t* this_mon = xmalloc(sizeof(*this_mon));
static_mons = xrealloc_n(static_mons, num_mons + 1, sizeof(*static_mons));
static_mons[num_mons] = this_mon;
num_mons++;
this_mon->svc = this_svc;
this_mon->idx = idx;
}
static void plugin_static_add_mon_addr(const char* desc V_UNUSED, const char* svc_name, const char* cname V_UNUSED, const gdnsd_anysin_t* addr V_UNUSED, const unsigned idx)
{
add_mon_any(svc_name, idx);
}
static void plugin_static_add_mon_cname(const char* desc V_UNUSED, const char* svc_name, const char* cname V_UNUSED, const unsigned idx)
{
add_mon_any(svc_name, idx);
}
static void plugin_static_init_monitors(struct ev_loop* mon_loop V_UNUSED)
{
for (unsigned i = 0; i < num_mons; i++)
gdnsd_mon_sttl_updater(static_mons[i]->idx, static_mons[i]->svc->static_sttl);
}
plugin_t plugin_static_funcs = {
.name = "static",
.config_loaded = false,
.used = false,
.load_config = plugin_static_load_config,
.map_res = plugin_static_map_res,
.pre_run = NULL,
.iothread_init = NULL,
.iothread_cleanup = NULL,
.resolve = plugin_static_resolve,
.add_svctype = plugin_static_add_svctype,
.add_mon_addr = plugin_static_add_mon_addr,
.add_mon_cname = plugin_static_add_mon_cname,
.init_monitors = plugin_static_init_monitors,
.start_monitors = NULL,
};