]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/sstuff.hh
Merge pull request #5794 from Habbie/azerty
[thirdparty/pdns.git] / pdns / sstuff.hh
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 #ifndef SSTUFF_HH
23 #define SSTUFF_HH
24
25 #include <string>
26 #include <sstream>
27 #include <iostream>
28 #include "iputils.hh"
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <sys/select.h>
36 #include <fcntl.h>
37 #include <stdexcept>
38
39 #include <boost/utility.hpp>
40 #include <csignal>
41 #include "namespaces.hh"
42 #include "namespaces.hh"
43
44
45 class NetworkError : public runtime_error
46 {
47 public:
48 NetworkError(const string& why="Network Error") : runtime_error(why.c_str())
49 {}
50 NetworkError(const char *why="Network Error") : runtime_error(why)
51 {}
52 };
53
54
55 typedef int ProtocolType; //!< Supported protocol types
56
57 //! Representation of a Socket and many of the Berkeley functions available
58 class Socket : public boost::noncopyable
59 {
60 Socket(int fd)
61 {
62 d_socket = fd;
63 d_buflen=4096;
64 d_buffer=new char[d_buflen];
65 }
66
67 public:
68 //! Construct a socket of specified address family and socket type.
69 Socket(int af, int st, ProtocolType pt=0)
70 {
71 if((d_socket=socket(af,st, pt))<0)
72 throw NetworkError(strerror(errno));
73 setCloseOnExec(d_socket);
74
75 d_buflen=4096;
76 d_buffer=new char[d_buflen];
77 }
78
79 ~Socket()
80 {
81 try {
82 closesocket(d_socket);
83 }
84 catch(const PDNSException& e) {
85 }
86
87 delete[] d_buffer;
88 }
89
90 //! If the socket is capable of doing so, this function will wait for a connection
91 Socket *accept()
92 {
93 struct sockaddr_in remote;
94 socklen_t remlen=sizeof(remote);
95 memset(&remote, 0, sizeof(remote));
96 int s=::accept(d_socket,(sockaddr *)&remote, &remlen);
97 if(s<0) {
98 if(errno==EAGAIN)
99 return nullptr;
100
101 throw NetworkError("Accepting a connection: "+string(strerror(errno)));
102 }
103
104 return new Socket(s);
105 }
106
107 //! Get remote address
108 bool getRemote(ComboAddress &remote) {
109 socklen_t remotelen=sizeof(remote);
110 return (getpeername(d_socket, (struct sockaddr *)&remote, &remotelen) >= 0);
111 }
112
113 //! Check remote address against netmaskgroup ng
114 bool acl(NetmaskGroup &ng)
115 {
116 ComboAddress remote;
117 if (getRemote(remote))
118 return ng.match((ComboAddress *) &remote);
119
120 return false;
121 }
122
123 //! Set the socket to non-blocking
124 void setNonBlocking()
125 {
126 ::setNonBlocking(d_socket);
127 }
128 //! Set the socket to blocking
129 void setBlocking()
130 {
131 ::setBlocking(d_socket);
132 }
133
134 void setReuseAddr()
135 {
136 int tmp = 1;
137 if (setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&tmp, static_cast<unsigned>(sizeof tmp))<0)
138 throw NetworkError(string("Setsockopt failed: ")+strerror(errno));
139 }
140
141 //! Bind the socket to a specified endpoint
142 void bind(const ComboAddress &local)
143 {
144 int tmp=1;
145 if(setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
146 throw NetworkError(string("Setsockopt failed: ")+strerror(errno));
147
148 if(::bind(d_socket,(struct sockaddr *)&local, local.getSocklen())<0)
149 throw NetworkError("While binding: "+string(strerror(errno)));
150 }
151
152 #if 0
153 //! Bind the socket to a specified endpoint
154 void bind(const ComboAddress &ep)
155 {
156 ComboAddress local;
157 memset(reinterpret_cast<char *>(&local),0,sizeof(local));
158 local.sin_family=d_family;
159 local.sin_addr.s_addr=ep.address.byte;
160 local.sin_port=htons(ep.port);
161
162 bind(local);
163 }
164 #endif
165 //! Connect the socket to a specified endpoint
166 void connect(const ComboAddress &ep, int timeout=0)
167 {
168 if(::connect(d_socket,(struct sockaddr *)&ep, ep.getSocklen()) < 0) {
169 if(errno == EINPROGRESS) {
170 if (timeout > 0) {
171 /* if a timeout is provided, we wait until the connection has been established */
172 int res = waitForRWData(d_socket, false, timeout, 0);
173 if (res == 0) {
174 throw NetworkError("timeout while connecting to "+ep.toStringWithPort());
175 } else if (res < 0) {
176 throw NetworkError("while waiting to connect to "+ep.toStringWithPort()+": "+string(strerror(errno)));
177 }
178 }
179 }
180 else {
181 throw NetworkError("While connecting to "+ep.toStringWithPort()+": "+string(strerror(errno)));
182 }
183 }
184 }
185
186
187 //! For datagram sockets, receive a datagram and learn where it came from
188 /** For datagram sockets, receive a datagram and learn where it came from
189 \param dgram Will be filled with the datagram
190 \param ep Will be filled with the origin of the datagram */
191 void recvFrom(string &dgram, ComboAddress &ep)
192 {
193 socklen_t remlen=sizeof(ep);
194 ssize_t bytes;
195 if((bytes=recvfrom(d_socket, d_buffer, d_buflen, 0, (sockaddr *)&ep , &remlen)) <0)
196 throw NetworkError("After recvfrom: "+string(strerror(errno)));
197
198 dgram.assign(d_buffer,bytes);
199 }
200
201 bool recvFromAsync(string &dgram, ComboAddress &ep)
202 {
203 struct sockaddr_in remote;
204 socklen_t remlen=sizeof(remote);
205 ssize_t bytes;
206 if((bytes=recvfrom(d_socket, d_buffer, d_buflen, 0, (sockaddr *)&remote, &remlen))<0) {
207 if(errno!=EAGAIN) {
208 throw NetworkError("After async recvfrom: "+string(strerror(errno)));
209 }
210 else {
211 return false;
212 }
213 }
214 dgram.assign(d_buffer,bytes);
215 return true;
216 }
217
218
219 //! For datagram sockets, send a datagram to a destination
220 void sendTo(const char* msg, size_t len, const ComboAddress &ep)
221 {
222 if(sendto(d_socket, msg, len, 0, (sockaddr *)&ep, ep.getSocklen())<0)
223 throw NetworkError("After sendto: "+string(strerror(errno)));
224 }
225
226 //! For connected datagram sockets, send a datagram
227 void send(const std::string& msg)
228 {
229 if(::send(d_socket, msg.c_str(), msg.size(), 0)<0)
230 throw NetworkError("After send: "+string(strerror(errno)));
231 }
232
233
234 /** For datagram sockets, send a datagram to a destination
235 \param dgram The datagram
236 \param ep The intended destination of the datagram */
237 void sendTo(const string &dgram, const ComboAddress &ep)
238 {
239 sendTo(dgram.c_str(), dgram.length(), ep);
240 }
241
242
243 //! Write this data to the socket, taking care that all bytes are written out
244 void writen(const string &data)
245 {
246 if(data.empty())
247 return;
248
249 size_t toWrite=data.length();
250 ssize_t res;
251 const char *ptr=data.c_str();
252
253 do {
254 res=::send(d_socket, ptr, toWrite, 0);
255 if(res<0)
256 throw NetworkError("Writing to a socket: "+string(strerror(errno)));
257 if(!res)
258 throw NetworkError("EOF on socket");
259 toWrite-=(size_t)res;
260 ptr+=(size_t)res;
261 }while(toWrite);
262
263 }
264
265 //! tries to write toWrite bytes from ptr to the socket
266 /** tries to write toWrite bytes from ptr to the socket, but does not make sure they al get written out
267 \param ptr Location to write from
268 \param toWrite number of bytes to try
269 */
270 size_t tryWrite(const char *ptr, size_t toWrite)
271 {
272 ssize_t res;
273 res=::send(d_socket,ptr,toWrite,0);
274 if(res==0)
275 throw NetworkError("EOF on writing to a socket");
276
277 if(res>0)
278 return res;
279
280 if(errno==EAGAIN)
281 return 0;
282
283 throw NetworkError("Writing to a socket: "+string(strerror(errno)));
284 }
285
286 //! Writes toWrite bytes from ptr to the socket
287 /** Writes toWrite bytes from ptr to the socket. Returns how many bytes were written */
288 size_t write(const char *ptr, size_t toWrite)
289 {
290 ssize_t res;
291 res=::send(d_socket,ptr,toWrite,0);
292 if(res<0) {
293 throw NetworkError("Writing to a socket: "+string(strerror(errno)));
294 }
295 return res;
296 }
297
298 void writenWithTimeout(const void *buffer, size_t n, int timeout)
299 {
300 size_t bytes=n;
301 const char *ptr = (char*)buffer;
302 ssize_t ret;
303 while(bytes) {
304 ret=::write(d_socket, ptr, bytes);
305 if(ret < 0) {
306 if(errno==EAGAIN) {
307 ret=waitForRWData(d_socket, false, timeout, 0);
308 if(ret < 0)
309 throw NetworkError("Waiting for data write");
310 if(!ret)
311 throw NetworkError("Timeout writing data");
312 continue;
313 }
314 else
315 throw NetworkError("Writing data: "+stringerror());
316 }
317 if(!ret) {
318 throw NetworkError("Did not fulfill TCP write due to EOF");
319 }
320
321 ptr += (size_t) ret;
322 bytes -= (size_t) ret;
323 }
324 }
325
326 //! reads one character from the socket
327 int getChar()
328 {
329 char c;
330
331 ssize_t res=::recv(d_socket,&c,1,0);
332 if(res)
333 return c;
334 return -1;
335 }
336
337 void getline(string &data)
338 {
339 data="";
340 int c;
341 while((c=getChar())!=-1) {
342 data+=(char)c;
343 if(c=='\n')
344 break;
345 }
346 }
347
348 //! Reads a block of data from the socket to a string
349 void read(string &data)
350 {
351 ssize_t res=::recv(d_socket,d_buffer,d_buflen,0);
352 if(res<0)
353 throw NetworkError("Reading from a socket: "+string(strerror(errno)));
354 data.assign(d_buffer,res);
355 }
356
357 //! Reads a block of data from the socket to a block of memory
358 size_t read(char *buffer, size_t bytes)
359 {
360 ssize_t res=::recv(d_socket,buffer,bytes,0);
361 if(res<0)
362 throw NetworkError("Reading from a socket: "+string(strerror(errno)));
363 return (size_t) res;
364 }
365
366 ssize_t readWithTimeout(char* buffer, size_t n, int timeout)
367 {
368 int err = waitForRWData(d_socket, true, timeout, 0);
369
370 if(err == 0)
371 throw NetworkError("timeout reading");
372 if(err < 0)
373 throw NetworkError("nonblocking read failed: "+string(strerror(errno)));
374
375 return read(buffer, n);
376 }
377
378 //! Sets the socket to listen with a default listen backlog of 10 pending connections
379 void listen(unsigned int length=10)
380 {
381 if(::listen(d_socket,length)<0)
382 throw NetworkError("Setting socket to listen: "+string(strerror(errno)));
383 }
384
385 //! Returns the internal file descriptor of the socket
386 int getHandle() const
387 {
388 return d_socket;
389 }
390
391 private:
392 char *d_buffer;
393 int d_socket;
394 size_t d_buflen;
395 };
396
397
398 #endif