]>
Commit | Line | Data |
---|---|---|
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 |
33 | UnixsocketConnector::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 | ||
48 | UnixsocketConnector::~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 |
59 | int 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 | 67 | int 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 | |
103 | ssize_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 | ||
124 | ssize_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 | ||
143 | void 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 | } |