GCC Code Coverage Report


Directory: ./
File: pdserv/src/TCP.cpp
Date: 2024-12-29 04:08:32
Exec Total Coverage
Lines: 111 148 75.0%
Branches: 56 175 32.0%

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