GCC Code Coverage Report


Directory: ./
File: pdserv/src/TCP.cpp
Date: 2025-08-17 04:10:43
Exec Total Coverage
Lines: 112 149 75.2%
Branches: 58 179 32.4%

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