root/trunk/pdns/pdns/session.cc @ 991

Revision 991, 8.8 KB (checked in by ahu, 6 years ago)

fix ticket 82, webserver not verbose enough with errors

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2002  PowerDNS.COM BV
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License version 2
7    as published by the Free Software Foundation
8   
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18*/
19#include "utility.hh"
20#include "session.hh"
21#include "ahuexception.hh"
22#include "misc.hh"
23#include <cstring>
24#include <iostream>
25#include <sys/types.h>
26#include <fcntl.h>
27#include <sstream>
28
29
30
31void Session::init()
32{
33  d_bufsize=15049;
34
35  d_verbose=false;
36
37  rdbuf=new char[d_bufsize]; 
38  rdoffset=0;
39  wroffset=0;
40}
41
42void Session::beVerbose()
43{
44  d_verbose=true;
45}
46
47Session::Session(int s, struct sockaddr_in r)
48{
49  init();
50  remote=r;
51  clisock=s;
52}
53
54int Session::close()
55{
56  int rc=0;
57 
58  if(clisock>=0)
59    rc=Utility::closesocket(clisock);
60
61  clisock=-1;
62  return rc;
63}
64
65Session::~Session()
66{
67
68  /* NOT CLOSING AUTOMATICALLY ANYMORE!
69    if(clisock>=0)
70    ::close(clisock);
71  */ 
72
73  delete[] rdbuf;
74}
75
76//! This function makes a deep copy of Session
77Session::Session(const Session &s)
78{
79  d_bufsize=s.d_bufsize;
80
81  init(); // needs d_bufsize, but will reset rdoffset & wroffset
82
83  rdoffset=s.rdoffset;
84  wroffset=s.wroffset;
85  clisock=s.clisock;
86  remote=s.remote;
87
88  memcpy(rdbuf,s.rdbuf,d_bufsize);
89} 
90
91Session::Session(const string &dest, int port, int timeout)
92{
93  struct hostent *h;
94  h=gethostbyname(dest.c_str());
95  if(!h)
96    throw SessionException("Unable to resolve target name");
97 
98  if(timeout)
99    d_timeout=timeout;
100
101  doConnect(*(int*)h->h_addr, port);
102}
103
104Session::Session(uint32_t ip, int port, int timeout)
105{
106  if(timeout)
107    d_timeout=timeout;
108
109  doConnect(ip, port);
110}
111
112void Session::setTimeout(unsigned int seconds)
113{
114  d_timeout=seconds;
115}
116
117 
118void Session::doConnect(uint32_t ip, int port)
119{
120  init();
121  clisock=socket(AF_INET,SOCK_STREAM,0);
122 
123  memset(&remote,0,sizeof(remote));
124  remote.sin_family=AF_INET;
125  remote.sin_port=htons(port);
126
127  remote.sin_addr.s_addr=ip;
128
129  Utility::setNonBlocking( clisock );
130
131  int err;
132#ifndef WIN32
133  if((err=connect(clisock,(struct sockaddr*)&remote,sizeof(remote)))<0 && errno!=EINPROGRESS) {
134#else
135  if((err=connect(clisock,(struct sockaddr*)&remote,sizeof(remote)))<0 && WSAGetLastError() != WSAEWOULDBLOCK ) {
136#endif // WIN32
137    throw SessionException("connect: "+stringerror());
138  }
139
140  if(!err)
141    goto done;
142
143  fd_set rset,wset;
144  struct timeval tval;
145
146  FD_ZERO(&rset);
147  FD_SET(clisock, &rset);
148  wset=rset;
149  tval.tv_sec=d_timeout;
150  tval.tv_usec=0;
151
152  if(!select(clisock+1,&rset,&wset,0,tval.tv_sec ? &tval : 0))
153    {
154      Utility::closesocket(clisock); // timeout
155      clisock=-1;
156      errno=ETIMEDOUT;
157 
158      throw SessionTimeoutException("Timeout connecting to server");
159    }
160 
161  if(FD_ISSET(clisock, &rset) || FD_ISSET(clisock, &wset))
162    {
163    Utility::socklen_t len=sizeof(err);
164      if(getsockopt(clisock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
165        throw SessionException("Error connecting: "+stringerror()); // Solaris
166
167      if(err)
168        throw SessionException("Error connecting: "+string(strerror(err)));
169
170    }
171  else
172    throw SessionException("nonblocking connect failed");
173
174 done:
175      Utility::setBlocking( clisock );
176}
177
178bool Session::putLine(const string &s)
179{
180  int length=s.length();
181  int written=0;
182  int err;
183
184  while(written<length)
185    {
186      fd_set wset;
187      FD_ZERO(&wset);
188      FD_SET(clisock, &wset);
189      struct timeval tval;
190      tval.tv_sec=d_timeout;
191      tval.tv_usec=0;
192     
193      if(!select(clisock+1,0,&wset,0,tval.tv_sec ? &tval : 0))
194        throw SessionTimeoutException("timeout writing line");
195
196      if(FD_ISSET(clisock, &wset))
197        {
198        Utility::socklen_t len=sizeof(err);
199          if(getsockopt(clisock, SOL_SOCKET,SO_ERROR,(char *) &err,&len)<0)
200            throw SessionException(+strerror(err)); // Solaris..
201         
202          if(err)
203            throw SessionException(strerror(err));
204        }
205      else
206        throw SessionException("nonblocking write failed"+string(strerror(errno)));
207
208      err=send(clisock,s.c_str()+written,length-written,0);
209
210      if(err<0)
211        return false;
212     
213      written+=err;
214    }
215
216  return true;
217}
218
219char *strnchr(char *p, char c, int len)
220{
221  int n;
222  for(n=0;n<len;n++)
223    if(p[n]==c)
224      return p+n;
225  return 0;
226}
227
228int Session::timeoutRead(int s, char *buf, size_t len)
229{
230  fd_set rset;
231  FD_ZERO(&rset);
232  FD_SET(clisock, &rset);
233  struct timeval tval;
234  tval.tv_sec=d_timeout;
235  tval.tv_usec=0;
236 
237  int err;
238 
239  if(!select(clisock+1,&rset,0,0,tval.tv_sec ? &tval : 0))
240    throw SessionTimeoutException("timeout reading");
241 
242  if(FD_ISSET(clisock, &rset))
243    {
244    Utility::socklen_t len=sizeof(err);
245      if(getsockopt(clisock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
246        throw SessionException(strerror(errno)); // Solaris..
247     
248      if(err)
249        throw SessionException(strerror(err));
250    }
251  else
252    throw SessionException("nonblocking read failed"+string(strerror(errno)));
253 
254  return recv(s,buf,len,0);
255     
256
257}
258
259bool 
260Session::haveLine()
261{
262  return (wroffset!=rdoffset && (strnchr(rdbuf+rdoffset,'\n',wroffset-rdoffset)!=NULL));
263}
264       
265
266bool 
267Session::getLine(string &line)
268{
269  int bytes;
270  char *p;
271
272  int linelength;
273 
274  // read data into a buffer
275  // find first \n, and return that as string, store how far we were
276
277
278  for(;;)
279    {
280      if(wroffset==rdoffset)
281        {
282          wroffset=rdoffset=0;
283        }
284
285      if(wroffset!=rdoffset && (p=strnchr(rdbuf+rdoffset,'\n',wroffset-rdoffset))) // we have a full line in store, return that
286        {
287          // from rdbuf+rdoffset to p should become the new line
288
289          linelength=p-(rdbuf+rdoffset); 
290         
291          *p=0; // terminate
292         
293          line=rdbuf+rdoffset;
294          line+="\n";
295         
296          rdoffset+=linelength+1;
297
298          return true;
299        }
300      // we need more data before we can return a line
301
302      if(wroffset==d_bufsize) // buffer is full, flush to left
303        {
304          if(!rdoffset) // line too long!
305            {
306              // FIXME: do stuff
307              close();
308              return false;
309            }
310
311          memmove(rdbuf,rdbuf+rdoffset,wroffset-rdoffset);
312          wroffset-=rdoffset;
313          rdoffset=0;
314        }
315      bytes=timeoutRead(clisock,rdbuf+wroffset,d_bufsize-wroffset);
316
317      if(bytes<0)
318          throw SessionException("error on read from socket: "+string(strerror(errno)));
319
320      if(bytes==0)
321        throw SessionException("Remote closed connection");
322
323      wroffset+=bytes;
324    }
325  // we never get here
326}
327 
328int Session::getSocket()
329{
330  return clisock;
331}
332
333string Session::getRemote ()
334{
335  ostringstream o;
336  uint32_t rint=htonl(remote.sin_addr.s_addr);
337  o<< (rint>>24 & 0xff)<<".";
338  o<< (rint>>16 & 0xff)<<".";
339  o<< (rint>>8  & 0xff)<<".";
340  o<< (rint     & 0xff);
341  o<<":"<<htons(remote.sin_port);
342
343  return o.str();
344}
345
346uint32_t Session::getRemoteAddr()
347{
348
349  return htonl(remote.sin_addr.s_addr);
350}
351
352string Session::getRemoteIP()
353{
354  ostringstream o;
355  uint32_t rint=htonl(remote.sin_addr.s_addr);
356  o<< (rint>>24 & 0xff)<<".";
357  o<< (rint>>16 & 0xff)<<".";
358  o<< (rint>>8  & 0xff)<<".";
359  o<< (rint     & 0xff);
360
361  return o.str();
362}
363 
364
365Session *Server::accept()
366{
367  struct sockaddr_in remote;
368  Utility::socklen_t len=sizeof(remote);
369
370  int clisock=-1;
371
372
373  while((clisock=::accept(s,(struct sockaddr *)(&remote),&len))==-1) // repeat until we have a succesful connect
374    {
375      //      L<<Logger::Error<<"accept() returned: "<<strerror(errno)<<endl;
376      if(errno==EMFILE) {
377        throw SessionException("Out of file descriptors - won't recover from that");
378      }
379
380    }
381
382  return new Session(clisock, remote);
383}
384
385
386
387Server::Server(int p, const string &p_localaddress)
388{
389  d_localaddress="0.0.0.0";
390  string localaddress=p_localaddress;
391  port=p;
392
393  struct sockaddr_in local;
394  s=socket(AF_INET,SOCK_STREAM,0);
395
396  if(s<0)
397    throw Exception(string("socket: ")+strerror(errno));
398 
399  memset(&local,0,sizeof(local));
400 
401  local.sin_family=AF_INET;
402 
403  struct hostent *h;
404  if(localaddress=="")
405    localaddress=d_localaddress;
406
407  if ( localaddress != "0.0.0.0" )
408  {
409    h=gethostbyname(localaddress.c_str());
410
411    if(!h)
412      throw Exception(); 
413 
414    local.sin_addr.s_addr=*(int*)h->h_addr;
415  }
416  else
417  {
418    local.sin_addr.s_addr = INADDR_ANY;
419  }
420
421  local.sin_port=htons(port);
422 
423  int tmp=1;
424  if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
425    throw SessionException(string("Setsockopt failed: ")+strerror(errno));
426
427  if(bind(s, (sockaddr*)&local,sizeof(local))<0)
428      throw SessionException("binding to port "+itoa(port)+string(" on ")+localaddress+": "+strerror(errno));
429 
430  if(listen(s,128)<0)
431      throw SessionException("listen: "+stringerror());
432
433}
434
Note: See TracBrowser for help on using the browser.