GCC Code Coverage Report


Directory: ./
File: pdserv/src/TCP.cpp
Date: 2026-05-10 04:09:05
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 374 TCPSocket::TCPSocket(): fd(-1)
46 {
47 374 }
48
49 /////////////////////////////////////////////////////////////////////////////
50 748 TCPSocket::~TCPSocket()
51 {
52 374 close();
53 374 }
54
55 /////////////////////////////////////////////////////////////////////////////
56 225 bool TCPSocket::listenTo(
57 const std::string& interface, const std::string& port, int backlog)
58 {
59 225 struct addrinfo hints;
60 225 ::memset(&hints, 0, sizeof hints);
61
1/2
✓ Branch 2 taken 225 times.
✗ Branch 3 not taken.
225 hints.ai_family = interface.empty() ? AF_INET : AF_UNSPEC;
62 225 hints.ai_socktype = SOCK_STREAM; /* TCP socket */
63 225 hints.ai_protocol = 0; /* Any protocol */
64 225 hints.ai_flags = AI_PASSIVE;
65
66 225 struct addrinfo *result;
67
1/2
✓ Branch 3 taken 225 times.
✗ Branch 4 not taken.
225 int s = ::getaddrinfo(interface.empty() ? NULL : interface.c_str(),
68
1/2
✓ Branch 2 taken 225 times.
✗ Branch 3 not taken.
450 port.c_str(), &hints, &result);
69
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 225 times.
225 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 225 times.
✗ Branch 3 not taken.
225 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 225 const char* errfunc = 0;
86 450 for (struct addrinfo* rp = result;
87
4/6
✓ Branch 0 taken 225 times.
✓ Branch 1 taken 225 times.
✓ Branch 2 taken 225 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 225 times.
✗ Branch 6 not taken.
675 rp and !errfunc and fd < 0; rp = rp->ai_next) {
88
89 225 fd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
90
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 225 times.
225 if (fd < 0)
91 continue;
92
93 // Reuse port
94 225 int optval = 1;
95
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 225 times.
450 if (::setsockopt(fd,
96 225 SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval)) {
97 errfunc = "setsockopt";
98 }
99
2/2
✓ Branch 4 taken 220 times.
✓ Branch 5 taken 5 times.
225 else if (!::bind(fd, rp->ai_addr, rp->ai_addrlen)) {
100
1/2
✓ Branch 2 taken 220 times.
✗ Branch 3 not taken.
220 if (!::listen(fd, backlog)) {
101 220 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 225 times.
✗ Branch 6 not taken.
5 close();
109 }
110
111 225 ::freeaddrinfo(result);
112
113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 225 times.
225 if (errfunc)
114 throw std::string(errfunc).append("(): ").append(::strerror(errno));
115
116 225 return fd >= 0;
117 }
118
119 /////////////////////////////////////////////////////////////////////////////
120 604 void TCPSocket::close()
121 {
122 604 int cancel_state;
123
1/2
✓ Branch 1 taken 604 times.
✗ Branch 2 not taken.
604 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
124
2/2
✓ Branch 1 taken 374 times.
✓ Branch 2 taken 230 times.
604 if (fd >= 0)
125
1/2
✓ Branch 2 taken 374 times.
✗ Branch 3 not taken.
374 ::close(fd);
126 604 fd = -1;
127
1/2
✓ Branch 1 taken 604 times.
✗ Branch 2 not taken.
604 pthread_setcancelstate(cancel_state, nullptr);
128 604 }
129
130 /////////////////////////////////////////////////////////////////////////////
131 TCPSocket::operator bool() const
132 {
133 return fd >= 0;
134 }
135
136 /////////////////////////////////////////////////////////////////////////////
137 21856 bool TCPSocket::readable(struct timeval *timeout) const
138 {
139 21856 fd_set readfds;
140 21856 FD_ZERO(&readfds);
141
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 21856 times.
21856 FD_SET(fd, &readfds);
142
1/2
✓ Branch 2 taken 21856 times.
✗ Branch 3 not taken.
21856 return ::select(fd + 1, &readfds, 0, 0, timeout) > 0;
143 }
144
145 299 TCPSocket* TCPSocket::select(std::vector<TCPSocket*> const& sockets, int msec)
146 {
147 299 struct timeval timeout;
148 299 timeout.tv_sec = msec / 1000;
149 299 timeout.tv_usec = 1000*(msec - 1000*timeout.tv_sec);
150
151 299 fd_set fds;
152 299 FD_ZERO(&fds);
153 299 int max_fd = -1;
154
2/2
✓ Branch 5 taken 434 times.
✓ Branch 6 taken 299 times.
733 for (TCPSocket *s : sockets)
155 {
156
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 434 times.
434 FD_SET(s->fd, &fds);
157 434 max_fd = std::max(max_fd, s->fd);
158 }
159
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 299 times.
✓ Branch 3 taken 149 times.
✓ Branch 4 taken 150 times.
299 ::select(max_fd + 1, &fds, nullptr, nullptr,
160 msec > -1 ? &timeout : nullptr);
161
4/8
✓ Branch 5 taken 215 times.
✗ Branch 6 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 149 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 149 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 149 times.
215 for (TCPSocket *s : sockets)
162 {
163
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 215 times.
✓ Branch 6 taken 149 times.
✓ Branch 7 taken 66 times.
215 if (FD_ISSET(s->fd, &fds))
164 149 return s;
165 }
166 return nullptr;
167 }
168
169 /////////////////////////////////////////////////////////////////////////////
170 149 std::string TCPSocket::accept(TCPSocket* server)
171 {
172 149 struct sockaddr_storage sa;
173 149 socklen_t len = sizeof sa;
174 149 struct sockaddr* addr = (struct sockaddr*)&sa;
175
176
1/2
✓ Branch 2 taken 149 times.
✗ Branch 3 not taken.
149 fd = ::accept(server->fd, addr, &len);
177
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 149 times.
149 if (fd < 0)
178 throw std::string("accept() on ")
179 .append(server->getAddr())
180 .append(": ")
181 .append(strerror(errno));
182
183 149 this->addr = *addr;
184
185 // Set server FQDN
186 149 char host[NI_MAXHOST];
187
2/4
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 149 times.
✗ Branch 4 not taken.
149 if (!::getnameinfo(addr, len,
188 host, NI_MAXHOST, 0, 0, NI_NUMERICSERV))
189
1/2
✓ Branch 3 taken 149 times.
✗ Branch 4 not taken.
149 return host;
190
191 return "Unknown";
192 }
193
194 /////////////////////////////////////////////////////////////////////////////
195 298 void TCPSocket::setSockOpt(int level, int opt, int val)
196 {
197
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 298 times.
298 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 298 }
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 434 std::string TCPSocket::getAddr(char sep) const
216 {
217 868 std::string host;
218 434 uint16_t port = 0;
219
220
1/3
✓ Branch 1 taken 434 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
434 switch (addr.sa_family) {
221 434 case AF_INET:
222 {
223 434 struct sockaddr_in *s = (struct sockaddr_in*)&addr;
224 434 char str[INET_ADDRSTRLEN];
225
1/2
✓ Branch 1 taken 434 times.
✗ Branch 2 not taken.
434 if (::inet_ntop(AF_INET, &s->sin_addr, str, sizeof str)) {
226
1/2
✓ Branch 1 taken 434 times.
✗ Branch 2 not taken.
434 host = str;
227 434 port = ntohs(s->sin_port);
228 434 }
229 }
230 434 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 434 times.
✗ Branch 3 not taken.
868 std::ostringstream os;
244
1/2
✓ Branch 2 taken 434 times.
✗ Branch 3 not taken.
434 os << port;
245
3/6
✓ Branch 2 taken 434 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 434 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 434 times.
✗ Branch 13 not taken.
868 return host + sep + os.str();
246 }
247
248 /////////////////////////////////////////////////////////////////////////////
249 14699 ssize_t TCPSocket::readData(void *buf, size_t len)
250 {
251 14699 int rv = ::read(fd, buf, len);
252
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14699 times.
14699 return rv >= 0 ? rv : -errno;;
253 }
254
255 /////////////////////////////////////////////////////////////////////////////
256 1539 ssize_t TCPSocket::writeData(const void *buf, size_t len)
257 {
258 1539 int rv = ::send(fd, buf, len, MSG_NOSIGNAL);
259
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1539 times.
1539 return rv >= 0 ? rv : -errno;;
260 }
261
262 /////////////////////////////////////////////////////////////////////////////
263 /////////////////////////////////////////////////////////////////////////////
264 225 TCPServer::TCPServer(int backlog): backlog(backlog)
265 {
266 225 }
267
268 /////////////////////////////////////////////////////////////////////////////
269 225 bool TCPServer::listenTo(
270 const std::string& interface, const std::string& port)
271 {
272 225 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 149 TCPSession::TCPSession(TCPServer *server):
294
2/4
✓ Branch 5 taken 149 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 149 times.
✗ Branch 10 not taken.
149 pthread::Thread("pdserv-tcp")
295 {
296
1/2
✓ Branch 2 taken 149 times.
✗ Branch 3 not taken.
149 peerName = accept(server);
297 149 }
298
299 /////////////////////////////////////////////////////////////////////////////
300 149 std::string TCPSession::getPeerName() const
301 {
302 149 return peerName;
303 }
304
305 /////////////////////////////////////////////////////////////////////////////
306 21856 bool TCPSession::isPendingRead(int msec) const
307 {
308 21856 struct timeval timeval;
309 21856 timeval.tv_sec = msec / 1000;
310 21856 timeval.tv_usec = (msec - timeval.tv_sec*1000) * 1000;
311
312
2/4
✓ Branch 1 taken 21856 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21856 times.
✗ Branch 5 not taken.
21856 return readable(msec < -1 ? 0 : &timeval);
313 }
314
315 /////////////////////////////////////////////////////////////////////////////
316 14699 ssize_t TCPSession::readData(void *buf, size_t len)
317 {
318 14699 return TCPSocket::readData(buf, len);
319 }
320
321 /////////////////////////////////////////////////////////////////////////////
322 1539 ssize_t TCPSession::writeData(const void *buf, size_t len)
323 {
324 1539 return TCPSocket::writeData(buf, len);
325 }
326