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