]>
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 | */ | |
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 |
45 | typedef int ProtocolType; //!< Supported protocol types |
46 | ||
47 | //! Representation of a Socket and many of the Berkeley functions available | |
fdf05fd4 | 48 | class 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 | 57 | public: |
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 | ||
368 | private: | |
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 |