]> git.ipfire.org Git - thirdparty/pdns.git/blame - modules/remotebackend/unixconnector.cc
Merge pull request #9106 from omoerbeek/release-cycles
[thirdparty/pdns.git] / modules / remotebackend / unixconnector.cc
CommitLineData
12471842
PL
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 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
96349507
CH
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/un.h>
bf42c817 28#include "remotebackend.hh"
bf42c817
PD
29#ifndef UNIX_PATH_MAX
30#define UNIX_PATH_MAX 108
31#endif
32
2010ac95
RG
33UnixsocketConnector::UnixsocketConnector(std::map<std::string,std::string> optionsMap) {
34 if (optionsMap.count("path") == 0) {
e6a9dde5 35 g_log<<Logger::Error<<"Cannot find 'path' option in connection string"<<endl;
74d13635
AT
36 throw PDNSException();
37 }
38 this->timeout = 2000;
2010ac95
RG
39 if (optionsMap.find("timeout") != optionsMap.end()) {
40 this->timeout = std::stoi(optionsMap.find("timeout")->second);
74d13635 41 }
2010ac95
RG
42 this->path = optionsMap.find("path")->second;
43 this->options = optionsMap;
74d13635
AT
44 this->connected = false;
45 this->fd = -1;
bf42c817
PD
46}
47
48UnixsocketConnector::~UnixsocketConnector() {
74d13635 49 if (this->connected) {
737a287f 50 try {
e6a9dde5 51 g_log<<Logger::Info<<"closing socket connection"<<endl;
737a287f
RG
52 }
53 catch (...) {
54 }
55 close(fd);
74d13635 56 }
bf42c817
PD
57}
58
fa2dd0e6
AT
59int UnixsocketConnector::send_message(const Json& input) {
60 auto data = input.dump() + "\n";
50e2c25c 61 int rv = this->write(data);
74d13635
AT
62 if (rv == -1)
63 return -1;
64 return rv;
bf42c817
PD
65}
66
fa2dd0e6 67int UnixsocketConnector::recv_message(Json& output) {
b16ac882 68 int rv;
fa2dd0e6 69 std::string s_output,err;
74d13635
AT
70
71 struct timeval t0,t;
72
74d13635
AT
73 gettimeofday(&t0, NULL);
74 memcpy(&t,&t0,sizeof(t0));
75 s_output = "";
76
77 while((t.tv_sec - t0.tv_sec)*1000 + (t.tv_usec - t0.tv_usec)/1000 < this->timeout) {
d85c547c
WO
78 int avail = waitForData(this->fd, 0, this->timeout * 500); // use half the timeout as poll timeout
79 if (avail < 0) // poll error
80 return -1;
81 if (avail == 0) { // timeout
82 gettimeofday(&t, NULL);
83 continue;
84 }
85
b16ac882 86 rv = this->read(s_output);
74d13635
AT
87 if (rv == -1)
88 return -1;
89
90 if (rv>0) {
fa2dd0e6
AT
91 // see if it can be parsed
92 output = Json::parse(s_output, err);
93 if (output != nullptr) return s_output.size();
74d13635
AT
94 }
95 gettimeofday(&t, NULL);
96 }
97
98 close(fd);
99 connected = false; // we need to reconnect
100 return -1;
bf42c817 101}
f4644dfc
PD
102
103ssize_t UnixsocketConnector::read(std::string &data) {
74d13635
AT
104 ssize_t nread;
105 char buf[1500] = {0};
f4644dfc 106
74d13635
AT
107 reconnect();
108 if (!connected) return -1;
109 nread = ::read(this->fd, buf, sizeof buf);
f4644dfc 110
74d13635
AT
111 // just try again later...
112 if (nread==-1 && errno == EAGAIN) return 0;
f4644dfc 113
630f9fdc 114 if (nread==-1 || nread==0) {
74d13635
AT
115 connected = false;
116 close(fd);
117 return -1;
118 }
f4644dfc 119
74d13635
AT
120 data.append(buf, nread);
121 return nread;
f4644dfc
PD
122}
123
124ssize_t UnixsocketConnector::write(const std::string &data) {
b16ac882 125 size_t pos = 0;
74d13635
AT
126
127 reconnect();
128 if (!connected) return -1;
b16ac882 129
74d13635 130 while(pos < data.size()) {
b16ac882
RG
131 ssize_t written = ::write(fd, &data.at(pos), data.size() - pos);
132 if (written < 1) {
74d13635
AT
133 connected = false;
134 close(fd);
135 return -1;
b16ac882
RG
136 } else {
137 pos = pos + static_cast<size_t>(written);
f4644dfc 138 }
74d13635 139 }
b16ac882 140 return pos;
f4644dfc
PD
141}
142
143void UnixsocketConnector::reconnect() {
74d13635 144 struct sockaddr_un sock;
74d13635
AT
145 int rv;
146
147 if (connected) return; // no point reconnecting if connected...
148 connected = true;
149
e6a9dde5 150 g_log<<Logger::Info<<"Reconnecting to backend" << std::endl;
74d13635
AT
151 fd = socket(AF_UNIX, SOCK_STREAM, 0);
152 if (fd < 0) {
153 connected = false;
e6a9dde5 154 g_log<<Logger::Error<<"Cannot create socket: " << strerror(errno) << std::endl;;
74d13635
AT
155 return;
156 }
157
158 if (makeUNsockaddr(path, &sock)) {
e6a9dde5 159 g_log<<Logger::Error<<"Unable to create UNIX domain socket: Path '"<<path<<"' is not a valid UNIX socket path."<<std::endl;
74d13635
AT
160 return;
161 }
162
d85c547c 163 rv = connect(fd, reinterpret_cast<struct sockaddr*>(&sock), sizeof sock);
74d13635
AT
164
165 if (rv != 0 && errno != EISCONN && errno != 0) {
e6a9dde5 166 g_log<<Logger::Error<<"Cannot connect to socket: " << strerror(errno) << std::endl;
74d13635
AT
167 close(fd);
168 connected = false;
169 return;
170 }
171 // send initialize
172
fa2dd0e6
AT
173 Json::array parameters;
174 Json msg = Json(Json::object{
175 { "method", "initialize" },
176 { "parameters", Json(options) },
177 });
74d13635 178
fa2dd0e6
AT
179 this->send(msg);
180 msg = nullptr;
181 if (this->recv(msg) == false) {
e6a9dde5 182 g_log<<Logger::Warning << "Failed to initialize backend" << std::endl;
74d13635
AT
183 close(fd);
184 this->connected = false;
185 }
f4644dfc 186}