]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/rec_channel.cc
Merge pull request #8096 from mind04/pdns-notify-db-queries
[thirdparty/pdns.git] / pdns / rec_channel.cc
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 #include "rec_channel.hh"
5 #include "utility.hh"
6 #include <sys/socket.h>
7 #include <cerrno>
8 #include "misc.hh"
9 #include <string.h>
10 #include <cstdlib>
11 #include <unistd.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <iostream>
15
16 #include "pdnsexception.hh"
17
18 #include "namespaces.hh"
19
20 volatile sig_atomic_t RecursorControlChannel::stop = 0;
21
22 RecursorControlChannel::RecursorControlChannel()
23 {
24 d_fd=-1;
25 *d_local.sun_path=0;
26 d_local.sun_family=0;
27 }
28
29 RecursorControlChannel::~RecursorControlChannel()
30 {
31 if(d_fd > 0)
32 close(d_fd);
33 if(*d_local.sun_path)
34 unlink(d_local.sun_path);
35 }
36
37 static void setSocketBuffer(int fd, int optname, uint32_t size)
38 {
39 uint32_t psize=0;
40 socklen_t len=sizeof(psize);
41
42 if (getsockopt(fd, SOL_SOCKET, optname, (void*)&psize, &len))
43 throw PDNSException("Unable to getsocket buffer size: "+stringerror());
44
45 if (psize > size)
46 return;
47
48 // failure to raise is not fatal
49 setsockopt(fd, SOL_SOCKET, optname, (const void*)&size, sizeof(size));
50 }
51
52
53 static void setSocketReceiveBuffer(int fd, uint32_t size)
54 {
55 setSocketBuffer(fd, SO_RCVBUF, size);
56 }
57
58 static void setSocketSendBuffer(int fd, uint32_t size)
59 {
60 setSocketBuffer(fd, SO_SNDBUF, size);
61 }
62
63 int RecursorControlChannel::listen(const string& fname)
64 {
65 d_fd=socket(AF_UNIX,SOCK_DGRAM,0);
66 setCloseOnExec(d_fd);
67
68 if(d_fd < 0)
69 throw PDNSException("Creating UNIX domain socket: "+stringerror());
70
71 int tmp=1;
72 if(setsockopt(d_fd, SOL_SOCKET, SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
73 throw PDNSException("Setsockopt failed: "+stringerror());
74
75 int err=unlink(fname.c_str());
76 if(err < 0 && errno!=ENOENT)
77 throw PDNSException("Can't remove (previous) controlsocket '"+fname+"': "+stringerror() + " (try --socket-dir)");
78
79 if(makeUNsockaddr(fname, &d_local))
80 throw PDNSException("Unable to bind to controlsocket, path '"+fname+"' is not a valid UNIX socket path.");
81
82 if(bind(d_fd, (sockaddr*)&d_local,sizeof(d_local))<0)
83 throw PDNSException("Unable to bind to controlsocket '"+fname+"': "+stringerror());
84
85 // receive buf should be size of max datagram plus address size
86 setSocketReceiveBuffer(d_fd, 60 * 1024);
87 setSocketSendBuffer(d_fd, 64 * 1024);
88
89 return d_fd;
90 }
91
92 void RecursorControlChannel::connect(const string& path, const string& fname)
93 {
94 struct sockaddr_un remote;
95
96 d_fd=socket(AF_UNIX,SOCK_DGRAM,0);
97 setCloseOnExec(d_fd);
98
99 if(d_fd < 0)
100 throw PDNSException("Creating UNIX domain socket: "+stringerror());
101
102 try {
103 int tmp=1;
104 if(setsockopt(d_fd, SOL_SOCKET, SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
105 throw PDNSException("Setsockopt failed: "+stringerror());
106
107 string localname=path+"/lsockXXXXXX";
108 *d_local.sun_path=0;
109 if (makeUNsockaddr(localname, &d_local))
110 throw PDNSException("Unable to bind to local temporary file, path '"+localname+"' is not a valid UNIX socket path.");
111
112 if(mkstemp(d_local.sun_path) < 0)
113 throw PDNSException("Unable to generate local temporary file in directory '"+path+"': "+stringerror());
114
115 int err=unlink(d_local.sun_path);
116 if(err < 0 && errno!=ENOENT)
117 throw PDNSException("Unable to remove local controlsocket: "+stringerror());
118
119 if(bind(d_fd, (sockaddr*)&d_local,sizeof(d_local))<0)
120 throw PDNSException("Unable to bind to local temporary file: "+stringerror());
121
122 if(chmod(d_local.sun_path,0666)<0) // make sure that pdns can reply!
123 throw PDNSException("Unable to chmod local temporary socket: "+stringerror());
124
125 string remotename=path+"/"+fname;
126 if (makeUNsockaddr(remotename, &remote))
127 throw PDNSException("Unable to connect to controlsocket, path '"+remotename+"' is not a valid UNIX socket path.");
128
129 if(::connect(d_fd, (sockaddr*)&remote, sizeof(remote)) < 0) {
130 if(*d_local.sun_path)
131 unlink(d_local.sun_path);
132 throw PDNSException("Unable to connect to remote '"+string(remote.sun_path)+"': "+stringerror());
133 }
134
135 // receive buf should be size of max datagram plus address size
136 setSocketReceiveBuffer(d_fd, 60 * 1024);
137 setSocketSendBuffer(d_fd, 64 * 1024);
138
139 } catch (...) {
140 close(d_fd);
141 d_fd=-1;
142 d_local.sun_path[0]=0;
143 throw;
144 }
145 }
146
147 void RecursorControlChannel::send(const std::string& msg, const std::string* remote, unsigned int timeout)
148 {
149 int ret = waitForRWData(d_fd, false, timeout, 0);
150 if(ret == 0) {
151 throw PDNSException("Timeout sending message over control channel");
152 }
153 else if(ret < 0) {
154 throw PDNSException("Error sending message over control channel:" + stringerror());
155 }
156
157 if(remote) {
158 struct sockaddr_un remoteaddr;
159 memset(&remoteaddr, 0, sizeof(remoteaddr));
160
161 remoteaddr.sun_family=AF_UNIX;
162 strncpy(remoteaddr.sun_path, remote->c_str(), sizeof(remoteaddr.sun_path)-1);
163 remoteaddr.sun_path[sizeof(remoteaddr.sun_path)-1] = '\0';
164
165 if(::sendto(d_fd, msg.c_str(), msg.length(), 0, (struct sockaddr*) &remoteaddr, sizeof(remoteaddr) ) < 0)
166 throw PDNSException("Unable to send message over control channel '"+string(remoteaddr.sun_path)+"': "+stringerror());
167 }
168 else if(::send(d_fd, msg.c_str(), msg.length(), 0) < 0)
169 throw PDNSException("Unable to send message over control channel: "+stringerror());
170 }
171
172 string RecursorControlChannel::recv(std::string* remote, unsigned int timeout)
173 {
174 char buffer[16384];
175 ssize_t len;
176 struct sockaddr_un remoteaddr;
177 socklen_t addrlen=sizeof(remoteaddr);
178
179 int ret=waitForData(d_fd, timeout, 0);
180 if(ret==0)
181 throw PDNSException("Timeout waiting for answer from control channel");
182
183 if( ret < 0 || (len=::recvfrom(d_fd, buffer, sizeof(buffer), 0, (struct sockaddr*)&remoteaddr, &addrlen)) < 0)
184 throw PDNSException("Unable to receive message over control channel: "+stringerror());
185
186 if(remote)
187 *remote=remoteaddr.sun_path;
188
189 return string(buffer, buffer+len);
190 }
191