]> git.ipfire.org Git - thirdparty/pdns.git/blob - modules/remotebackend/unixconnector.cc
updated KSK and ZSK Rollover procedures, small fixes in Algorithm Rollover procedure
[thirdparty/pdns.git] / modules / remotebackend / unixconnector.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include "remotebackend.hh"
29 #ifndef UNIX_PATH_MAX
30 #define UNIX_PATH_MAX 108
31 #endif
32
33 UnixsocketConnector::UnixsocketConnector(std::map<std::string, std::string> optionsMap)
34 {
35 if (optionsMap.count("path") == 0) {
36 g_log << Logger::Error << "Cannot find 'path' option in connection string" << endl;
37 throw PDNSException();
38 }
39 this->timeout = 2000;
40 if (optionsMap.find("timeout") != optionsMap.end()) {
41 this->timeout = std::stoi(optionsMap.find("timeout")->second);
42 }
43 this->path = optionsMap.find("path")->second;
44 this->options = optionsMap;
45 this->connected = false;
46 this->fd = -1;
47 }
48
49 UnixsocketConnector::~UnixsocketConnector()
50 {
51 if (this->connected) {
52 try {
53 g_log << Logger::Info << "closing socket connection" << endl;
54 }
55 catch (...) {
56 }
57 close(fd);
58 }
59 }
60
61 int UnixsocketConnector::send_message(const Json& input)
62 {
63 auto data = input.dump() + "\n";
64 int rv = this->write(data);
65 if (rv == -1)
66 return -1;
67 return rv;
68 }
69
70 int UnixsocketConnector::recv_message(Json& output)
71 {
72 int rv;
73 std::string s_output, err;
74
75 struct timeval t0, t;
76
77 gettimeofday(&t0, NULL);
78 memcpy(&t, &t0, sizeof(t0));
79 s_output = "";
80
81 while ((t.tv_sec - t0.tv_sec) * 1000 + (t.tv_usec - t0.tv_usec) / 1000 < this->timeout) {
82 int avail = waitForData(this->fd, 0, this->timeout * 500); // use half the timeout as poll timeout
83 if (avail < 0) // poll error
84 return -1;
85 if (avail == 0) { // timeout
86 gettimeofday(&t, NULL);
87 continue;
88 }
89
90 rv = this->read(s_output);
91 if (rv == -1)
92 return -1;
93
94 if (rv > 0) {
95 // see if it can be parsed
96 output = Json::parse(s_output, err);
97 if (output != nullptr)
98 return s_output.size();
99 }
100 gettimeofday(&t, NULL);
101 }
102
103 close(fd);
104 connected = false; // we need to reconnect
105 return -1;
106 }
107
108 ssize_t UnixsocketConnector::read(std::string& data)
109 {
110 ssize_t nread;
111 char buf[1500] = {0};
112
113 reconnect();
114 if (!connected)
115 return -1;
116 nread = ::read(this->fd, buf, sizeof buf);
117
118 // just try again later...
119 if (nread == -1 && errno == EAGAIN)
120 return 0;
121
122 if (nread == -1 || nread == 0) {
123 connected = false;
124 close(fd);
125 return -1;
126 }
127
128 data.append(buf, nread);
129 return nread;
130 }
131
132 ssize_t UnixsocketConnector::write(const std::string& data)
133 {
134 size_t pos = 0;
135
136 reconnect();
137 if (!connected)
138 return -1;
139
140 while (pos < data.size()) {
141 ssize_t written = ::write(fd, &data.at(pos), data.size() - pos);
142 if (written < 1) {
143 connected = false;
144 close(fd);
145 return -1;
146 }
147 else {
148 pos = pos + static_cast<size_t>(written);
149 }
150 }
151 return pos;
152 }
153
154 void UnixsocketConnector::reconnect()
155 {
156 struct sockaddr_un sock;
157 int rv;
158
159 if (connected)
160 return; // no point reconnecting if connected...
161 connected = true;
162
163 g_log << Logger::Info << "Reconnecting to backend" << std::endl;
164 fd = socket(AF_UNIX, SOCK_STREAM, 0);
165 if (fd < 0) {
166 connected = false;
167 g_log << Logger::Error << "Cannot create socket: " << strerror(errno) << std::endl;
168 ;
169 return;
170 }
171
172 if (makeUNsockaddr(path, &sock)) {
173 g_log << Logger::Error << "Unable to create UNIX domain socket: Path '" << path << "' is not a valid UNIX socket path." << std::endl;
174 return;
175 }
176
177 rv = connect(fd, reinterpret_cast<struct sockaddr*>(&sock), sizeof sock);
178
179 if (rv != 0 && errno != EISCONN && errno != 0) {
180 g_log << Logger::Error << "Cannot connect to socket: " << strerror(errno) << std::endl;
181 close(fd);
182 connected = false;
183 return;
184 }
185 // send initialize
186
187 Json::array parameters;
188 Json msg = Json(Json::object{
189 {"method", "initialize"},
190 {"parameters", Json(options)},
191 });
192
193 this->send(msg);
194 msg = nullptr;
195 if (this->recv(msg) == false) {
196 g_log << Logger::Warning << "Failed to initialize backend" << std::endl;
197 close(fd);
198 this->connected = false;
199 }
200 }