1
#include "tests/uds_server.h"
2

            
3
#include <sys/socket.h>
4
#include <sys/types.h>
5
#include <sys/un.h>
6
#include <unistd.h>
7

            
8
#include <cerrno>
9
#include <functional>
10
#include <memory>
11
#include <string>
12

            
13
#include "envoy/common/exception.h"
14

            
15
#include "source/common/common/logger.h"
16
#include "source/common/common/utility.h"
17
#include "source/common/network/address_impl.h"
18

            
19
#include "test/test_common/thread_factory_for_test.h"
20

            
21
namespace Envoy {
22

            
23
UDSServer::UDSServer(const std::string& path, std::function<void(const std::string&)> cb)
24
125
    : msg_cb_(cb), addr_(THROW_OR_RETURN_VALUE(Network::Address::PipeInstance::create(path),
25
125
                                               std::unique_ptr<Network::Address::PipeInstance>)),
26
125
      fd2_(-1) {
27
125
  ENVOY_LOG(trace, "Creating unix domain socket server: {}", addr_->asStringView());
28
125
  if (!addr_->pipe()->abstractNamespace()) {
29
124
    ::unlink(addr_->asString().c_str());
30
124
  }
31
125
  fd_ = ::socket(AF_UNIX, SOCK_SEQPACKET, 0);
32
125
  if (fd_ == -1) {
33
    ENVOY_LOG(error, "Can't create socket: {}", Envoy::errorDetails(errno));
34
    return;
35
  }
36

            
37
125
  ENVOY_LOG(trace, "Binding to {}", addr_->asStringView());
38
125
  if (::bind(fd_, addr_->sockAddr(), addr_->sockAddrLen()) == -1) {
39
    ENVOY_LOG(warn, "Bind to {} failed: {}", addr_->asStringView(), Envoy::errorDetails(errno));
40
    close();
41
    return;
42
  }
43

            
44
125
  ENVOY_LOG(trace, "Listening on {}", addr_->asStringView());
45
125
  if (::listen(fd_, 5) == -1) {
46
    ENVOY_LOG(warn, "Listen on {} failed: {}", addr_->asStringView(), Envoy::errorDetails(errno));
47
    close();
48
    return;
49
  }
50

            
51
125
  ENVOY_LOG(trace, "Starting unix domain socket server thread fd: {}", fd_.load());
52

            
53
125
  thread_ = Thread::threadFactoryForTest().createThread([this]() { threadRoutine(); });
54
125
}
55

            
56
125
UDSServer::~UDSServer() {
57
125
  if (fd_ >= 0) {
58
125
    close();
59
125
    ENVOY_LOG(trace, "Waiting on unix domain socket server to close: {}",
60
125
              Envoy::errorDetails(errno));
61
125
    thread_->join();
62
125
    thread_.reset();
63
125
  }
64
125
}
65

            
66
125
void UDSServer::close() {
67
125
  ::shutdown(fd_, SHUT_RD);
68
125
  ::shutdown(fd2_, SHUT_RD);
69
125
  errno = 0;
70
125
  ::close(fd_);
71
125
  fd_ = -1;
72
125
  if (!addr_->pipe()->abstractNamespace()) {
73
124
    ::unlink(addr_->asString().c_str());
74
124
  }
75
125
}
76

            
77
125
void UDSServer::threadRoutine() {
78
217
  while (fd_ >= 0) {
79
138
    ENVOY_LOG(debug, "Unix domain socket server thread started on fd: {}", fd_.load());
80
    // Accept a new connection
81
138
    struct sockaddr_un addr;
82
138
    socklen_t addr_len = sizeof(addr);
83
138
    ENVOY_LOG(trace, "Unix domain socket server blocking accept on fd: {}", fd_.load());
84
138
    fd2_ = ::accept(fd_, reinterpret_cast<sockaddr*>(&addr), &addr_len);
85
138
    if (fd2_ < 0) {
86
46
      if (errno == EINVAL) {
87
46
        return; // fd_ was closed
88
46
      }
89
      ENVOY_LOG(warn, "Unix domain socket server accept on fd {} failed: {}", fd_.load(),
90
                Envoy::errorDetails(errno));
91
      continue;
92
46
    }
93
92
    char buf[8192];
94
319
    while (true) {
95
319
      ENVOY_LOG(trace, "Unix domain socket server blocking recv on fd: {}", fd2_.load());
96
319
      ssize_t received = ::recv(fd2_, buf, sizeof(buf), 0);
97
319
      if (received < 0) {
98
        if (errno == EINTR) {
99
          continue;
100
        }
101
        ENVOY_LOG(warn, "Unix domain socket server recv on fd {} failed: {}", fd2_.load(),
102
                  Envoy::errorDetails(errno));
103
        break;
104
319
      } else if (received == 0) {
105
92
        ENVOY_LOG(trace, "Unix domain socket server client on fd {} has closed socket",
106
92
                  fd2_.load());
107
92
        break;
108
227
      } else {
109
227
        std::string data(buf, received);
110
227
        if (msg_cb_) {
111
227
          msg_cb_(data);
112
227
        }
113
227
      }
114
319
    }
115
92
    ::close(fd2_);
116
92
    fd2_ = -1;
117
92
  }
118
125
}
119

            
120
} // namespace Envoy