You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

192 lines
5.7 KiB

  1. #ifdef HAVE_CONFIG_H
  2. #include "config.h"
  3. #endif
  4. #include "rec_channel.hh"
  5. #include "utility.hh"
  6. #include <sys/socket.h>
  7. #include <cerrno>
  8. #include "misc.hh"
  9. #include <string.h>
  10. #include <cstdlib>
  11. #include <unistd.h>
  12. #include <sys/types.h>
  13. #include <sys/stat.h>
  14. #include <iostream>
  15. #include "pdnsexception.hh"
  16. #include "namespaces.hh"
  17. volatile sig_atomic_t RecursorControlChannel::stop = 0;
  18. RecursorControlChannel::RecursorControlChannel()
  19. {
  20. d_fd=-1;
  21. *d_local.sun_path=0;
  22. d_local.sun_family=0;
  23. }
  24. RecursorControlChannel::~RecursorControlChannel()
  25. {
  26. if(d_fd > 0)
  27. close(d_fd);
  28. if(*d_local.sun_path)
  29. unlink(d_local.sun_path);
  30. }
  31. static void setSocketBuffer(int fd, int optname, uint32_t size)
  32. {
  33. uint32_t psize=0;
  34. socklen_t len=sizeof(psize);
  35. if (getsockopt(fd, SOL_SOCKET, optname, (void*)&psize, &len))
  36. throw PDNSException("Unable to getsocket buffer size: "+stringerror());
  37. if (psize > size)
  38. return;
  39. // failure to raise is not fatal
  40. setsockopt(fd, SOL_SOCKET, optname, (const void*)&size, sizeof(size));
  41. }
  42. static void setSocketReceiveBuffer(int fd, uint32_t size)
  43. {
  44. setSocketBuffer(fd, SO_RCVBUF, size);
  45. }
  46. static void setSocketSendBuffer(int fd, uint32_t size)
  47. {
  48. setSocketBuffer(fd, SO_SNDBUF, size);
  49. }
  50. int RecursorControlChannel::listen(const string& fname)
  51. {
  52. d_fd=socket(AF_UNIX,SOCK_DGRAM,0);
  53. setCloseOnExec(d_fd);
  54. if(d_fd < 0)
  55. throw PDNSException("Creating UNIX domain socket: "+stringerror());
  56. int tmp=1;
  57. if(setsockopt(d_fd, SOL_SOCKET, SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
  58. throw PDNSException("Setsockopt failed: "+stringerror());
  59. int err=unlink(fname.c_str());
  60. if(err < 0 && errno!=ENOENT)
  61. throw PDNSException("Can't remove (previous) controlsocket '"+fname+"': "+stringerror() + " (try --socket-dir)");
  62. if(makeUNsockaddr(fname, &d_local))
  63. throw PDNSException("Unable to bind to controlsocket, path '"+fname+"' is not a valid UNIX socket path.");
  64. if(bind(d_fd, (sockaddr*)&d_local,sizeof(d_local))<0)
  65. throw PDNSException("Unable to bind to controlsocket '"+fname+"': "+stringerror());
  66. // receive buf should be size of max datagram plus address size
  67. setSocketReceiveBuffer(d_fd, 60 * 1024);
  68. setSocketSendBuffer(d_fd, 64 * 1024);
  69. return d_fd;
  70. }
  71. void RecursorControlChannel::connect(const string& path, const string& fname)
  72. {
  73. struct sockaddr_un remote;
  74. d_fd=socket(AF_UNIX,SOCK_DGRAM,0);
  75. setCloseOnExec(d_fd);
  76. if(d_fd < 0)
  77. throw PDNSException("Creating UNIX domain socket: "+stringerror());
  78. try {
  79. int tmp=1;
  80. if(setsockopt(d_fd, SOL_SOCKET, SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
  81. throw PDNSException("Setsockopt failed: "+stringerror());
  82. string localname=path+"/lsockXXXXXX";
  83. *d_local.sun_path=0;
  84. if (makeUNsockaddr(localname, &d_local))
  85. throw PDNSException("Unable to bind to local temporary file, path '"+localname+"' is not a valid UNIX socket path.");
  86. if(mkstemp(d_local.sun_path) < 0)
  87. throw PDNSException("Unable to generate local temporary file in directory '"+path+"': "+stringerror());
  88. int err=unlink(d_local.sun_path);
  89. if(err < 0 && errno!=ENOENT)
  90. throw PDNSException("Unable to remove local controlsocket: "+stringerror());
  91. if(bind(d_fd, (sockaddr*)&d_local,sizeof(d_local))<0)
  92. throw PDNSException("Unable to bind to local temporary file: "+stringerror());
  93. if(chmod(d_local.sun_path,0666)<0) // make sure that pdns can reply!
  94. throw PDNSException("Unable to chmod local temporary socket: "+stringerror());
  95. string remotename=path+"/"+fname;
  96. if (makeUNsockaddr(remotename, &remote))
  97. throw PDNSException("Unable to connect to controlsocket, path '"+remotename+"' is not a valid UNIX socket path.");
  98. if(::connect(d_fd, (sockaddr*)&remote, sizeof(remote)) < 0) {
  99. if(*d_local.sun_path)
  100. unlink(d_local.sun_path);
  101. throw PDNSException("Unable to connect to remote '"+string(remote.sun_path)+"': "+stringerror());
  102. }
  103. // receive buf should be size of max datagram plus address size
  104. setSocketReceiveBuffer(d_fd, 60 * 1024);
  105. setSocketSendBuffer(d_fd, 64 * 1024);
  106. } catch (...) {
  107. close(d_fd);
  108. d_fd=-1;
  109. d_local.sun_path[0]=0;
  110. throw;
  111. }
  112. }
  113. void RecursorControlChannel::send(const std::string& msg, const std::string* remote, unsigned int timeout)
  114. {
  115. int ret = waitForRWData(d_fd, false, timeout, 0);
  116. if(ret == 0) {
  117. throw PDNSException("Timeout sending message over control channel");
  118. }
  119. else if(ret < 0) {
  120. throw PDNSException("Error sending message over control channel:" + stringerror());
  121. }
  122. if(remote) {
  123. struct sockaddr_un remoteaddr;
  124. memset(&remoteaddr, 0, sizeof(remoteaddr));
  125. remoteaddr.sun_family=AF_UNIX;
  126. strncpy(remoteaddr.sun_path, remote->c_str(), sizeof(remoteaddr.sun_path)-1);
  127. remoteaddr.sun_path[sizeof(remoteaddr.sun_path)-1] = '\0';
  128. if(::sendto(d_fd, msg.c_str(), msg.length(), 0, (struct sockaddr*) &remoteaddr, sizeof(remoteaddr) ) < 0)
  129. throw PDNSException("Unable to send message over control channel '"+string(remoteaddr.sun_path)+"': "+stringerror());
  130. }
  131. else if(::send(d_fd, msg.c_str(), msg.length(), 0) < 0)
  132. throw PDNSException("Unable to send message over control channel: "+stringerror());
  133. }
  134. string RecursorControlChannel::recv(std::string* remote, unsigned int timeout)
  135. {
  136. char buffer[16384];
  137. ssize_t len;
  138. struct sockaddr_un remoteaddr;
  139. socklen_t addrlen=sizeof(remoteaddr);
  140. int ret=waitForData(d_fd, timeout, 0);
  141. if(ret==0)
  142. throw PDNSException("Timeout waiting for answer from control channel");
  143. if( ret < 0 || (len=::recvfrom(d_fd, buffer, sizeof(buffer), 0, (struct sockaddr*)&remoteaddr, &addrlen)) < 0)
  144. throw PDNSException("Unable to receive message over control channel: "+stringerror());
  145. if(remote)
  146. *remote=remoteaddr.sun_path;
  147. return string(buffer, buffer+len);
  148. }