File: rec_channel.cc

package info (click to toggle)
pdns-recursor 4.1.11-1+deb10u1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 7,936 kB
  • sloc: cpp: 54,211; javascript: 26,587; sh: 11,872; makefile: 453; xml: 37
file content (147 lines) | stat: -rw-r--r-- 4,669 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "rec_channel.hh"
#include "utility.hh"
#include <sys/socket.h>
#include <cerrno>
#include "misc.hh"
#include <string.h>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <iostream>

#include "pdnsexception.hh"

#include "namespaces.hh"

RecursorControlChannel::RecursorControlChannel()
{
  d_fd=-1;
  *d_local.sun_path=0;
  d_local.sun_family=0;
}

RecursorControlChannel::~RecursorControlChannel() 
{
  if(d_fd > 0)
    close(d_fd);
  if(*d_local.sun_path)
    unlink(d_local.sun_path);
}

int RecursorControlChannel::listen(const string& fname)
{
  d_fd=socket(AF_UNIX,SOCK_DGRAM,0);
  setCloseOnExec(d_fd);

  if(d_fd < 0) 
    throw PDNSException("Creating UNIX domain socket: "+stringerror());
  
  int tmp=1;
  if(setsockopt(d_fd, SOL_SOCKET, SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
    throw PDNSException("Setsockopt failed: "+stringerror());
  
  int err=unlink(fname.c_str());
  if(err < 0 && errno!=ENOENT)
    throw PDNSException("Can't remove (previous) controlsocket '"+fname+"': "+stringerror() + " (try --socket-dir)");

  if(makeUNsockaddr(fname, &d_local))
    throw PDNSException("Unable to bind to controlsocket, path '"+fname+"' is not a valid UNIX socket path.");
    
  if(bind(d_fd, (sockaddr*)&d_local,sizeof(d_local))<0) 
    throw PDNSException("Unable to bind to controlsocket '"+fname+"': "+stringerror());

  return d_fd;
}

void RecursorControlChannel::connect(const string& path, const string& fname)
{
  struct sockaddr_un remote;

  d_fd=socket(AF_UNIX,SOCK_DGRAM,0);
  setCloseOnExec(d_fd);

  if(d_fd < 0) 
    throw PDNSException("Creating UNIX domain socket: "+string(strerror(errno)));

  try {
    int tmp=1;
    if(setsockopt(d_fd, SOL_SOCKET, SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
      throw PDNSException("Setsockopt failed: "+stringerror());
  
    string localname=path+"/lsockXXXXXX";
    *d_local.sun_path=0;
    if (makeUNsockaddr(localname, &d_local))
      throw PDNSException("Unable to bind to local temporary file, path '"+localname+"' is not a valid UNIX socket path.");

    if(mkstemp(d_local.sun_path) < 0)
      throw PDNSException("Unable to generate local temporary file in directory '"+path+"': "+stringerror());

    int err=unlink(d_local.sun_path);
    if(err < 0 && errno!=ENOENT)
      throw PDNSException("Unable to remove local controlsocket: "+stringerror());

    if(bind(d_fd, (sockaddr*)&d_local,sizeof(d_local))<0)
      throw PDNSException("Unable to bind to local temporary file: "+stringerror());

    if(chmod(d_local.sun_path,0666)<0) // make sure that pdns can reply!
      throw PDNSException("Unable to chmod local temporary socket: "+stringerror());

    string remotename=path+"/"+fname;
    if (makeUNsockaddr(remotename, &remote))
      throw PDNSException("Unable to connect to controlsocket, path '"+remotename+"' is not a valid UNIX socket path.");

    if(::connect(d_fd, (sockaddr*)&remote, sizeof(remote)) < 0) {
      if(*d_local.sun_path)
	unlink(d_local.sun_path);
      throw PDNSException("Unable to connect to remote '"+string(remote.sun_path)+"': "+stringerror());
    }

  } catch (...) {
    close(d_fd);
    d_fd=-1;
    d_local.sun_path[0]=0;
    throw;
  }
}

void RecursorControlChannel::send(const std::string& msg, const std::string* remote)
{
  if(remote) {
    struct sockaddr_un remoteaddr;
    memset(&remoteaddr, 0, sizeof(remoteaddr));
  
    remoteaddr.sun_family=AF_UNIX;
    strncpy(remoteaddr.sun_path, remote->c_str(), sizeof(remoteaddr.sun_path));
    remoteaddr.sun_path[sizeof(remoteaddr.sun_path)-1] = '\0';

    if(::sendto(d_fd, msg.c_str(), msg.length(), 0, (struct sockaddr*) &remoteaddr, sizeof(remoteaddr) ) < 0)
      throw PDNSException("Unable to send message over control channel '"+string(remoteaddr.sun_path)+"': "+string(strerror(errno)));
  }
  else if(::send(d_fd, msg.c_str(), msg.length(), 0) < 0)
    throw PDNSException("Unable to send message over control channel: "+string(strerror(errno)));
}

string RecursorControlChannel::recv(std::string* remote, unsigned int timeout)
{
  char buffer[16384];
  ssize_t len;
  struct sockaddr_un remoteaddr;
  socklen_t addrlen=sizeof(remoteaddr);
  
  int ret=waitForData(d_fd, timeout, 0);
  if(ret==0)
    throw PDNSException("Timeout waiting for answer from control channel");
  
  if( ret < 0 || (len=::recvfrom(d_fd, buffer, sizeof(buffer), 0, (struct sockaddr*)&remoteaddr, &addrlen)) < 0)
    throw PDNSException("Unable to receive message over control channel: "+string(strerror(errno)));

  if(remote)
    *remote=remoteaddr.sun_path;

  return string(buffer, buffer+len);
}