GCC Code Coverage Report


Directory: ./
File: pdserv/src/TCP.cpp
Date: 2023-11-12 04:06:57
Exec Total Coverage
Lines: 111 148 75.0%
Branches: 57 179 31.8%

Line Branch Exec Source
1 /*****************************************************************************
2 *
3 * $Id$
4 *
5 * Copyright 2017 Richard Hacker (lerichi at gmx dot net)
6 *
7 * This file is part of the pdserv library.
8 *
9 * The pdserv library is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published
11 * by the Free Software Foundation, either version 3 of the License, or (at
12 * your option) any later version.
13 *
14 * The pdserv library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17 * License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with the pdserv library. If not, see <http://www.gnu.org/licenses/>.
21 *
22 *****************************************************************************/
23
24 #include "TCP.h"
25 #include "Debug.h"
26
27 #include <cerrno>
28 #include <cstring> // memset()
29 #include <stdexcept>
30 #include <sstream>
31 #include <unistd.h> // close()
32 #include <sys/select.h> // select()
33 #include <sys/time.h> // struct timeval
34
35 // socket stuff: getaddrinfo(), gai_strerror(), connect(), bind(),
36 // setsockopt(), inet_ntop()
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <arpa/inet.h>
40 #include <netdb.h>
41 #include <pthread.h>
42
43 using namespace net;
44
45 /////////////////////////////////////////////////////////////////////////////
46 /////////////////////////////////////////////////////////////////////////////
47 312 TCPSocket::TCPSocket(): fd(-1)
48 {
49 312 }
50
51 /////////////////////////////////////////////////////////////////////////////
52 624 TCPSocket::~TCPSocket()
53 {
54 312 close();
55 312 }
56
57 /////////////////////////////////////////////////////////////////////////////
58 188 bool TCPSocket::listenTo(
59 const std::string& interface, const std::string& port, int backlog)
60 {
61 188 struct addrinfo hints;
62 188 ::memset(&hints, 0, sizeof hints);
63
1/2
✓ Branch 2 taken 188 times.
✗ Branch 3 not taken.
188 hints.ai_family = interface.empty() ? AF_INET : AF_UNSPEC;
64 188 hints.ai_socktype = SOCK_STREAM; /* TCP socket */
65 188 hints.ai_protocol = 0; /* Any protocol */
66 188 hints.ai_flags = AI_PASSIVE;
67
68 188 struct addrinfo *result;
69
1/2
✓ Branch 3 taken 188 times.
✗ Branch 4 not taken.
188 int s = ::getaddrinfo(interface.empty() ? NULL : interface.c_str(),
70
1/2
✓ Branch 2 taken 188 times.
✗ Branch 3 not taken.
376 port.c_str(), &hints, &result);
71
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 188 times.
188 if (s != 0) {
72 std::ostringstream os;
73 os << "getaddrinfo("
74 << "address=" << interface << ", "
75 << "port=" << port << "): "
76 << ::gai_strerror(s);
77 throw os.str();
78 }
79
80
1/2
✓ Branch 2 taken 188 times.
✗ Branch 3 not taken.
188 close();
81
82 /* getaddrinfo() returns a list of address structures.
83 Try each address until we successfully connect(2).
84 If socket(2) (or connect(2)) fails, we (close the socket
85 and) try the next address. */
86
87 188 const char* errfunc = 0;
88 376 for (struct addrinfo* rp = result;
89
4/6
✓ Branch 0 taken 188 times.
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 188 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 188 times.
✗ Branch 6 not taken.
564 rp and !errfunc and fd < 0; rp = rp->ai_next) {
90
91 188 fd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
92
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 188 times.
188 if (fd < 0)
93 continue;
94
95 // Reuse port
96 188 int optval = 1;
97
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 188 times.
376 if (::setsockopt(fd,
98 188 SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval)) {
99 errfunc = "setsockopt";
100 }
101
2/2
✓ Branch 4 taken 183 times.
✓ Branch 5 taken 5 times.
188 else if (!::bind(fd, rp->ai_addr, rp->ai_addrlen)) {
102
1/2
✓ Branch 2 taken 183 times.
✗ Branch 3 not taken.
183 if (!::listen(fd, backlog)) {
103 183 addr = *rp->ai_addr;
104 }
105 else {
106 errfunc = "listen";
107 }
108 }
109 else
110
2/4
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 188 times.
✗ Branch 6 not taken.
5 close();
111 }
112
113 188 ::freeaddrinfo(result);
114
115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 188 times.
188 if (errfunc)
116 throw std::string(errfunc).append("(): ").append(::strerror(errno));
117
118 188 return fd >= 0;
119 }
120
121 /////////////////////////////////////////////////////////////////////////////
122 505 void TCPSocket::close()
123 {
124 505 int cancel_state;
125
1/2
✓ Branch 1 taken 505 times.
✗ Branch 2 not taken.
505 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
126
2/2
✓ Branch 1 taken 312 times.
✓ Branch 2 taken 193 times.
505 if (fd >= 0)
127
1/2
✓ Branch 2 taken 312 times.
✗ Branch 3 not taken.
312 ::close(fd);
128 505 fd = -1;
129
1/2
✓ Branch 1 taken 505 times.
✗ Branch 2 not taken.
505 pthread_setcancelstate(cancel_state, nullptr);
130 505 }
131
132 /////////////////////////////////////////////////////////////////////////////
133 TCPSocket::operator bool() const
134 {
135 return fd >= 0;
136 }
137
138 /////////////////////////////////////////////////////////////////////////////
139 16400 bool TCPSocket::readable(struct timeval *timeout) const
140 {
141 16400 fd_set readfds;
142 16400 FD_ZERO(&readfds);
143
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16400 times.
16400 FD_SET(fd, &readfds);
144
1/2
✓ Branch 2 taken 16400 times.
✗ Branch 3 not taken.
16400 return ::select(fd + 1, &readfds, 0, 0, timeout) > 0;
145 }
146
147 249 TCPSocket* TCPSocket::select(std::vector<TCPSocket*> const& sockets, int msec)
148 {
149 249 struct timeval timeout;
150 249 timeout.tv_sec = msec / 1000;
151 249 timeout.tv_usec = 1000*(msec - 1000*timeout.tv_sec);
152
153 249 fd_set fds;
154 249 FD_ZERO(&fds);
155 249 int max_fd = -1;
156
2/2
✓ Branch 5 taken 360 times.
✓ Branch 6 taken 249 times.
609 for (TCPSocket *s : sockets)
157 {
158
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 360 times.
360 FD_SET(s->fd, &fds);
159 360 max_fd = std::max(max_fd, s->fd);
160 }
161
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 249 times.
✓ Branch 3 taken 124 times.
✓ Branch 4 taken 125 times.
249 ::select(max_fd + 1, &fds, nullptr, nullptr, msec > -1 ? &timeout : nullptr);
162
4/8
✓ Branch 5 taken 178 times.
✗ Branch 6 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 124 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 124 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 124 times.
178 for (TCPSocket *s : sockets)
163 {
164
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 178 times.
✓ Branch 6 taken 124 times.
✓ Branch 7 taken 54 times.
178 if (FD_ISSET(s->fd, &fds))
165 124 return s;
166 }
167 return nullptr;
168 }
169
170 /////////////////////////////////////////////////////////////////////////////
171 124 std::string TCPSocket::accept(TCPSocket* server)
172 {
173 124 struct sockaddr_storage sa;
174 124 socklen_t len = sizeof sa;
175 124 struct sockaddr* addr = (struct sockaddr*)&sa;
176
177
1/2
✓ Branch 2 taken 124 times.
✗ Branch 3 not taken.
124 fd = ::accept(server->fd, addr, &len);
178
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 124 times.
124 if (fd < 0)
179 throw std::string("accept() on ")
180 .append(server->getAddr())
181 .append(": ")
182 .append(strerror(errno));
183
184 124 this->addr = *addr;
185
186 // Set server FQDN
187 124 char host[NI_MAXHOST];
188
2/4
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 124 times.
✗ Branch 4 not taken.
124 if (!::getnameinfo(addr, len,
189 host, NI_MAXHOST, 0, 0, NI_NUMERICSERV))
190
1/2
✓ Branch 3 taken 124 times.
✗ Branch 4 not taken.
124 return host;
191
192 return "Unknown";
193 }
194
195 /////////////////////////////////////////////////////////////////////////////
196 248 void TCPSocket::setSockOpt(int level, int opt, int val)
197 {
198
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 248 times.
248 if (::setsockopt(fd, level, opt, &val, sizeof val)) {
199 throw std::string("setsockopt() on ")
200 .append(getAddr())
201 .append(": ")
202 .append(strerror(errno));
203 }
204 248 }
205
206 /////////////////////////////////////////////////////////////////////////////
207 std::string TCPSocket::reject()
208 {
209 TCPSocket s;
210 std::string name = s.accept(this);
211
212 return s.getAddr().append(" (").append(name).append(1,')');
213 }
214
215 /////////////////////////////////////////////////////////////////////////////
216 360 std::string TCPSocket::getAddr(char sep) const
217 {
218 720 std::string host;
219 360 uint16_t port = 0;
220
221
1/3
✓ Branch 1 taken 360 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
360 switch (addr.sa_family) {
222 360 case AF_INET:
223 {
224 360 struct sockaddr_in *s = (struct sockaddr_in*)&addr;
225 360 char str[INET_ADDRSTRLEN];
226
1/2
✓ Branch 1 taken 360 times.
✗ Branch 2 not taken.
360 if (::inet_ntop(AF_INET, &s->sin_addr, str, sizeof str)) {
227
1/2
✓ Branch 1 taken 360 times.
✗ Branch 2 not taken.
360 host = str;
228 360 port = ntohs(s->sin_port);
229 360 }
230 }
231 360 break;
232 case AF_INET6:
233 {
234 struct sockaddr_in6 *s = (struct sockaddr_in6*)&addr;
235 char str[INET6_ADDRSTRLEN];
236 if (::inet_ntop(AF_INET6, &s->sin6_addr, str, sizeof str)) {
237 host = str;
238 port = ntohs(s->sin6_port);
239 }
240 }
241 break;
242 }
243
244
1/2
✓ Branch 2 taken 360 times.
✗ Branch 3 not taken.
720 std::ostringstream os;
245
1/2
✓ Branch 2 taken 360 times.
✗ Branch 3 not taken.
360 os << port;
246
3/6
✓ Branch 2 taken 360 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 360 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 360 times.
✗ Branch 13 not taken.
720 return host + sep + os.str();
247 }
248
249 /////////////////////////////////////////////////////////////////////////////
250 11230 ssize_t TCPSocket::readData(void *buf, size_t len)
251 {
252 11230 int rv = ::read(fd, buf, len);
253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11230 times.
11230 return rv >= 0 ? rv : -errno;;
254 }
255
256 /////////////////////////////////////////////////////////////////////////////
257 1011 ssize_t TCPSocket::writeData(const void *buf, size_t len)
258 {
259 1011 int rv = ::send(fd, buf, len, MSG_NOSIGNAL);
260
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1011 times.
1011 return rv >= 0 ? rv : -errno;;
261 }
262
263 /////////////////////////////////////////////////////////////////////////////
264 /////////////////////////////////////////////////////////////////////////////
265 188 TCPServer::TCPServer(int backlog): backlog(backlog)
266 {
267 188 }
268
269 /////////////////////////////////////////////////////////////////////////////
270 188 bool TCPServer::listenTo(
271 const std::string& interface, const std::string& port)
272 {
273 188 return TCPSocket::listenTo(interface, port, backlog);
274 }
275
276 /////////////////////////////////////////////////////////////////////////////
277 bool TCPServer::isPendingConnection(int msec)
278 {
279 struct timeval timeout;
280 timeout.tv_sec = msec / 1000;
281 timeout.tv_usec = 1000*(msec - 1000*timeout.tv_sec);
282
283 return readable(msec < 0 ? 0 : &timeout);
284 }
285
286 /////////////////////////////////////////////////////////////////////////////
287 std::string TCPServer::reject()
288 {
289 return TCPSocket::reject();
290 }
291
292 /////////////////////////////////////////////////////////////////////////////
293 /////////////////////////////////////////////////////////////////////////////
294
1/2
✓ Branch 6 taken 124 times.
✗ Branch 7 not taken.
124 TCPSession::TCPSession(TCPServer *server)
295 {
296
1/2
✓ Branch 2 taken 124 times.
✗ Branch 3 not taken.
124 peerName = accept(server);
297 124 }
298
299 /////////////////////////////////////////////////////////////////////////////
300 124 std::string TCPSession::getPeerName() const
301 {
302 124 return peerName;
303 }
304
305 /////////////////////////////////////////////////////////////////////////////
306 16400 bool TCPSession::isPendingRead(int msec) const
307 {
308 16400 struct timeval timeval;
309 16400 timeval.tv_sec = msec / 1000;
310 16400 timeval.tv_usec = (msec - timeval.tv_sec*1000) * 1000;
311
312
2/4
✓ Branch 1 taken 16400 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16400 times.
✗ Branch 5 not taken.
16400 return readable(msec < -1 ? 0 : &timeval);
313 }
314
315 /////////////////////////////////////////////////////////////////////////////
316 11230 ssize_t TCPSession::readData(void *buf, size_t len)
317 {
318 11230 return TCPSocket::readData(buf, len);
319 }
320
321 /////////////////////////////////////////////////////////////////////////////
322 1011 ssize_t TCPSession::writeData(const void *buf, size_t len)
323 {
324 1011 return TCPSocket::writeData(buf, len);
325 }
326