plptools
Loading...
Searching...
No Matches
tcpsocket.cc
Go to the documentation of this file.
1/*
2 * This file is part of plptools.
3 *
4 * Copyright (C) 1999 Philip Proudman <philip.proudman@btinternet.com>
5 * Copyright (C) 1999-2001 Fritz Elfert <felfert@to.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * along with this program; if not, see <https://www.gnu.org/licenses/>.
19 *
20 */
21#include "config.h"
22
23#include "bufferstore.h"
24#include "tcpsocket.h"
25#include "iowatch.h"
26
27#include <cstring>
28#include <iostream>
29
30#include <assert.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <stdarg.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <errno.h>
37#include <ctype.h>
38#include <sys/time.h>
39#include <sys/types.h>
40#include <netinet/in.h>
41#include <arpa/inet.h>
42
43#define INVALID_SOCKET -1
44#define SOCKET_ERROR -1
45
46#ifndef MSG_NOSIGNAL
47#define MSG_NOSIGNAL 0
48#endif
49
50using namespace std;
51
53{
54 m_Socket = another.m_Socket;
55 m_HostAddr = another.m_HostAddr;
56 m_PeerAddr = another.m_PeerAddr;
57 m_Bound = another.m_Bound;
58 m_LastError = another.m_LastError;
59 myWatch = another.myWatch;
60}
61
62
64{
66
67 memset(&m_HostAddr, 0, sizeof(m_HostAddr));
68 memset(&m_PeerAddr, 0, sizeof(m_PeerAddr));
69
70 ((struct sockaddr_in *) &m_HostAddr)->sin_family = AF_INET;
71 ((struct sockaddr_in *) &m_PeerAddr)->sin_family = AF_INET;
72
73 m_Bound = false;
74 m_LastError = 0;
75 myWatch = 0L;
76}
77
79{
80 if (m_Socket != INVALID_SOCKET) {
81 if (myWatch)
83 shutdown(m_Socket, SHUT_RDWR);
84 ::close(m_Socket);
85 }
86}
87
89setWatch(IOWatch *watch) {
90 if (myWatch) {
92 }
93 myWatch = watch;
94 if (myWatch && (m_Socket != INVALID_SOCKET)) {
96 }
97}
98
100reconnect()
101{
102 if (m_Socket != INVALID_SOCKET) {
103 if (myWatch)
105 shutdown(m_Socket, SHUT_RDWR);
106 ::close(m_Socket);
107 }
109 if (!createSocket())
110 return (false);
111 m_LastError = 0;
112 m_Bound = false;
113 if (::bind(m_Socket, &m_HostAddr, sizeof(m_HostAddr)) != 0) {
114 m_LastError = errno;
115 return (false);
116 }
117 if (::connect(m_Socket, &m_PeerAddr, sizeof(m_PeerAddr)) != 0) {
118 m_LastError = errno;
119 return (false);
120 }
121 if (myWatch) {
123 }
124 return (true);
125}
126
128toString()
129{
130 string ret = "";
131 char nbuf[10];
132 char *tmp = 0L;
133
134 tmp = inet_ntoa(((struct sockaddr_in *) &m_HostAddr)->sin_addr);
135 ret += tmp ? tmp : "none:none";
136 if (tmp) {
137 ret += ':';
138 sprintf(nbuf, "%d", ntohs(((struct sockaddr_in *) &m_HostAddr)->sin_port));
139 ret += nbuf;
140 }
141 ret += " -> ";
142 tmp = inet_ntoa(((struct sockaddr_in *) &m_PeerAddr)->sin_addr);
143 ret += tmp ? tmp : "none:none";
144 if (tmp) {
145 ret += ':';
146 sprintf(nbuf, "%d", ntohs(((struct sockaddr_in *) &m_PeerAddr)->sin_port));
147 ret += nbuf;
148 }
149 return ret;
150}
151
153connect(const char * const Peer, int PeerPort, const char * const Host, int HostPort)
154{
155 //****************************************************
156 //* If we aren't already bound set the host and bind *
157 //****************************************************
158
159 if (!bindSocket(Host, HostPort)) {
160 if (m_LastError != 0) {
161 return (false);
162 }
163 }
164 //****************
165 //* Set the peer *
166 //****************
167 if (!setPeer(Peer, PeerPort)) {
168 return (false);
169 }
170 //***********
171 //* Connect *
172 //***********
173 if (::connect(m_Socket, &m_PeerAddr, sizeof(m_PeerAddr)) != 0) {
174 m_LastError = errno;
175 return (false);
176 }
177 if (myWatch)
179 return (true);
180}
181
183listen(const char * const Host, int Port)
184{
185 //****************************************************
186 //* If we aren't already bound set the host and bind *
187 //****************************************************
188
189 if (!bindSocket(Host, Port)) {
190 if (m_LastError != 0) {
191 return (false);
192 }
193 }
194 //**********************
195 //* Listen on the port *
196 //**********************
197
198 if (myWatch)
200 if (::listen(m_Socket, 5) != 0) {
201 m_LastError = errno;
202 return (false);
203 }
204 return (true);
205}
206
208accept(std::string *Peer)
209{
210 socklen_t len;
211
212 //*****************************************************
213 //* Allocate a new object to hold the accepted socket *
214 //*****************************************************
215 TCPSocket *accepted = new TCPSocket();
216
217 //***********************
218 //* Accept a connection *
219 //***********************
220
221 len = sizeof(struct sockaddr);
222 accepted->m_Socket = ::accept(m_Socket, &accepted->m_PeerAddr, &len);
223
224 if (accepted->m_Socket == INVALID_SOCKET) {
225 m_LastError = errno;
226 delete accepted;
227 return NULL;
228 }
229 //****************************************************
230 //* Got a connection so fill in the other attributes *
231 //****************************************************
232
233 // Make sure the new socket hasn't inherited O_NONBLOCK
234 // from the accept socket
235 int flags = fcntl(accepted->m_Socket, F_GETFL, 0);
236 flags &= ~O_NONBLOCK;
237 fcntl(accepted->m_Socket, F_SETFL, flags);
238
239 accepted->m_HostAddr = m_HostAddr;
240 accepted->m_Bound = true;
241
242 //****************************************************
243 //* If required get the name of the connected client *
244 //****************************************************
245 if (Peer) {
246 *Peer = inet_ntoa(((struct sockaddr_in *) &accepted->m_PeerAddr)->sin_addr);
247 }
248 return accepted;
249}
250
251TCPSocket *TCPSocket::accept(std::string *Peer, int cancellationFd) {
252 assert(m_Socket != INVALID_SOCKET);
253 while (true) {
254
255 fd_set fileDescriptorSet;
256 FD_ZERO(&fileDescriptorSet);
257 FD_SET(m_Socket, &fileDescriptorSet);
258 FD_SET(cancellationFd, &fileDescriptorSet);
259 int result = select(max(m_Socket, cancellationFd) + 1, &fileDescriptorSet, nullptr, nullptr, nullptr);
260
261 // Ignore interrupt.
262 if (result == -1 && errno == EINTR) {
263 continue;
264 }
265
266 // Respect cancellations.
267 if (FD_ISSET(cancellationFd, &fileDescriptorSet)) {
268 return nullptr;
269 }
270
271 // If we've got here, there should be a socket we can safely accept.
272 return accept(Peer);
273 }
274}
275
277dataToGet(int sec, int usec) const
278{
279 fd_set io;
280 FD_ZERO(&io);
281 FD_SET(m_Socket, &io);
282 struct timeval t;
283 t.tv_usec = usec;
284 t.tv_sec = sec;
285 return (select(m_Socket + 1, &io, NULL, NULL, &t) != 0) ? true : false;
286}
287
289getBufferStore(bufferStore & a, bool wait)
290{
291 /* Returns a 0 for for no message,
292 * 1 for message OK, and -1 for socket problem
293 */
294
295 uint32_t l;
296 long count = 0;
297 unsigned char *buff;
298 unsigned char *bp;
299 if (!wait && !dataToGet(0, 0))
300 return 0;
301 a.init();
302 if (recv(&l, sizeof(l), MSG_NOSIGNAL) != sizeof(l)) {
303 return -1;
304 }
305 l = ntohl(l);
306 if (l > 16384)
307 return -1;
308 bp = buff = new unsigned char[l];
309 while (l > 0) {
310 int j = recv(bp, l, MSG_NOSIGNAL);
311 if (j == SOCKET_ERROR || j == 0) {
312 delete[]buff;
313 return -1;
314 }
315 count += j;
316 l -= j;
317 bp += j;
318 };
319 a.init(buff, count);
320 delete[]buff;
321 return (a.getLen() == 0) ? 0 : 1;
322}
323
326{
327 long l = a.getLen();
328 uint32_t hl = htonl(l);
329 long sent = 0;
330 int retries = 0;
331 int i;
332
333 bufferStore b;
334 b.addBytes(reinterpret_cast<const unsigned char *>(&hl), sizeof(hl));
335 b.addBuff(a);
336 l += 4;
337 while (l > 0) {
338 i = send((const char *)b.getString(sent), l, MSG_NOSIGNAL);
339 if (i == SOCKET_ERROR || i == 0)
340 return (false);
341 sent += i;
342 l -= i;
343 if (++retries > 5) {
344 m_LastError = 0;
345 return (false);
346 }
347 }
348 return true;
349}
350
352recv(void *buf, int len, int flags)
353{
354 int i = ::recv(m_Socket, buf, len, flags);
355
356 if (i < 0)
357 m_LastError = errno;
358
359 return (i);
360}
361
363send(const void * const buf, int len, int flags)
364{
365 int i = ::send(m_Socket, buf, len, flags);
366
367 if (i < 0)
368 m_LastError = errno;
369
370 return (i);
371}
372
374closeSocket(void)
375{
376 if (myWatch)
378 shutdown(m_Socket, SHUT_RDWR);
379 if (::close(m_Socket) != 0) {
380 m_LastError = errno;
381 return false;
382 }
384 return true;
385}
386
388bindSocket(const char * const Host, int Port)
389{
390
391 // If we are already bound return false but with no last error
392 if (m_Bound) {
393 m_LastError = 0;
394 return false;
395 }
396
397 // If the socket hasn't been created create it now
398
399 if (m_Socket == INVALID_SOCKET) {
400 if (!createSocket())
401 return false;
402 }
403
404 // Set SO_REUSEADDR
405 int one = 1;
406 if (setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR,
407 (const char *)&one, sizeof(int)) < 0)
408 cerr << "Warning: Unable to set SO_REUSEADDR option\n";
409
410 // If a host name was supplied then use it
411 if (!setHost(Host, Port))
412 return false;
413
414 // Now bind the socket
415 if (::bind(m_Socket, &m_HostAddr, sizeof(m_HostAddr)) != 0) {
416 m_LastError = errno;
417 return false;
418 }
419
420 m_Bound = true;
421 return true;
422}
423
425bindInRange(const char * const Host, int Low, int High, int Retries)
426{
427 int port;
428 int i;
429
430 // If we are already bound return false but with no last error
431 if (m_Bound) {
432 m_LastError = 0;
433 return (false);
434 }
435
436 // If the socket hasn't been created create it now
437 if (m_Socket == INVALID_SOCKET) {
438 if (!createSocket())
439 return false;
440 }
441
442 // If the number of retries is greater than the range then work
443 // through the range sequentially.
444 if (Retries > High - Low) {
445 for (port = Low; port <= High; port++) {
446 if (!setHost(Host, port))
447 return false;
448 if (::bind(m_Socket, &m_HostAddr, sizeof(m_HostAddr)) == 0)
449 break;
450 }
451 if (port > High) {
452 m_LastError = errno;
453 return false;
454 }
455 } else {
456 for (i = 0; i < Retries; i++) {
457 port = Low + (rand() % (High - Low));
458 if (!setHost(Host, port))
459 return false;
460 if (::bind(m_Socket, &m_HostAddr, sizeof(m_HostAddr)) == 0)
461 break;
462 }
463 if (i >= Retries) {
464 m_LastError = errno;
465 return false;
466 }
467 }
468 m_Bound = true;
469 return true;
470}
471
473linger(bool LingerOn, int LingerTime)
474{
475 int i;
476 struct linger l;
477
478 // If the socket hasn't been created create it now
479 if (m_Socket == INVALID_SOCKET) {
480 if (!createSocket())
481 return false;
482 }
483
484 // Set the lingering
485 if (LingerOn) {
486 l.l_onoff = 1;
487 l.l_linger = LingerTime;
488 } else {
489 l.l_onoff = 0;
490 l.l_linger = 0;
491 }
492 i = setsockopt(m_Socket, SOL_SOCKET, SO_LINGER, (const char *) &l, sizeof(l));
493 // Check for errors
494 if (i != 0) {
495 m_LastError = errno;
496 return false;
497 }
498 return true;
499}
500
502createSocket(void)
503{
504 // If the socket has already been created just return true
506 return true;
507
508 // Create the socket
509 m_Socket = ::socket(PF_INET, SOCK_STREAM, 0);
510 if (m_Socket == INVALID_SOCKET) {
511 m_LastError = errno;
512 return false;
513 }
514
515 // By default set no lingering
516 linger(false);
517
518 // Return indicating success
519 return true;
520}
521
523setPeer(const char * const Peer, int Port)
524{
525 struct hostent *he = NULL;
526
527 // If a peer name was supplied then use it
528 if (Peer) {
529 if (!isdigit(Peer[0]))
530 // RFC1035 specifies that hostnames must not start
531 // with a digit. So we can speed up things here.
532 he = gethostbyname(Peer);
533 if (!he) {
534 struct in_addr ipaddr;
535
536 if (!inet_aton(Peer, &ipaddr)) {
537 m_LastError = errno;
538 return false;
539 }
540 he = gethostbyaddr((const char *)&ipaddr.s_addr, sizeof(ipaddr.s_addr), PF_INET);
541 if (!he) {
542 m_LastError = errno;
543 return (false);
544 }
545 }
546 memcpy(&((struct sockaddr_in *)&m_PeerAddr)->sin_addr, he->h_addr_list[0],
547 sizeof(((struct sockaddr_in *)&m_PeerAddr)->sin_addr));
548 }
549 // If a port name was supplied use it
550 if (Port > 0)
551 ((struct sockaddr_in *)&m_PeerAddr)->sin_port = htons(Port);
552 return true;
553}
554
556getPeer(string *Peer, int *Port)
557{
558 char *peer;
559
560 if (Peer) {
561 peer = inet_ntoa(((struct sockaddr_in *) &m_PeerAddr)->sin_addr);
562 if (!peer) {
563 m_LastError = errno;
564 return (false);
565 }
566 *Peer = peer;
567 }
568 if (Port)
569 *Port = ntohs(((struct sockaddr_in *) &m_PeerAddr)->sin_port);
570 return false;
571}
572
574setHost(const char * const Host, int Port)
575{
576 struct hostent *he;
577
578 // If a host name was supplied then use it
579 if (Host) {
580 if (!isdigit(Host[0]))
581 // RFC1035 specifies that hostnames must not start
582 // with a digit. So we can speed up things here.
583 he = gethostbyname(Host);
584 he = gethostbyname(Host);
585 if (!he) {
586 struct in_addr ipaddr;
587
588 if (!inet_aton(Host, &ipaddr)) {
589 m_LastError = errno;
590 return false;
591 }
592 he = gethostbyaddr((const char *)&ipaddr.s_addr, sizeof(ipaddr.s_addr), PF_INET);
593 if (!he) {
594 m_LastError = errno;
595 return false;
596 }
597 }
598 memcpy(&((struct sockaddr_in *)&m_HostAddr)->sin_addr, he->h_addr_list[0],
599 sizeof(((struct sockaddr_in *)&m_HostAddr)->sin_addr));
600 }
601
602 // If a port name was supplied use it
603 if (Port > 0)
604 ((struct sockaddr_in *)&m_HostAddr)->sin_port = htons(Port);
605 return true;
606}
607
609getHost(string *Host, int *Port)
610{
611 char *host;
612
613 if (Host) {
614 host = inet_ntoa(((struct sockaddr_in *)&m_HostAddr)->sin_addr);
615 if (!host) {
616 m_LastError = errno;
617 return false;
618 }
619 *Host = host;
620 }
621 if (Port)
622 *Port = ntohs(((struct sockaddr_in *)&m_HostAddr)->sin_port);
623 return true;
624}
A simple thread-safe wrapper for select()
Definition: iowatch.h:34
void remIO(const int fd)
Removes a file descriptor from the set of descriptors.
Definition: iowatch.cc:54
void addIO(const int fd)
Adds a file descriptor to the set of descriptors.
Definition: iowatch.cc:42
A class for dealing with sockets.
Definition: tcpsocket.h:38
bool bindInRange(const char *const Host, int Low, int High, int Retries)
Tries repeated binds to a local address and port.
Definition: tcpsocket.cc:425
IOWatch * myWatch
Definition: tcpsocket.h:234
int m_Socket
Definition: tcpsocket.h:230
bool closeSocket(void)
Closes the connection.
Definition: tcpsocket.cc:374
bool linger(bool LingerOn, int LingerTime=0)
Sets the linger parameter of the socket.
Definition: tcpsocket.cc:473
bool setPeer(const char *const Peer, int Port)
Definition: tcpsocket.cc:523
void setWatch(IOWatch *watch)
Registers an IOWatch for this socket.
Definition: tcpsocket.cc:89
virtual bool listen(const char *const Host, int Port)
Starts listening.
Definition: tcpsocket.cc:183
bool getPeer(std::string *Peer, int *Port)
Retrieves peer information.
Definition: tcpsocket.cc:556
virtual bool reconnect()
Reopens the connection after closing it.
Definition: tcpsocket.cc:100
bool setHost(const char *const Host, int Port)
Definition: tcpsocket.cc:574
struct sockaddr m_HostAddr
Definition: tcpsocket.h:228
TCPSocket()
Constructs a TCPSocket.
Definition: tcpsocket.cc:63
int m_LastError
Definition: tcpsocket.h:233
int getBufferStore(bufferStore &a, bool wait=true)
Receive data into a bufferStore .
Definition: tcpsocket.cc:289
bool sendBufferStore(const bufferStore &a)
Sends data from a bufferStore .
Definition: tcpsocket.cc:325
virtual bool createSocket(void)
Creates the socket.
Definition: tcpsocket.cc:502
bool getHost(std::string *Host, int *Port)
Retrieves local information.
Definition: tcpsocket.cc:609
virtual std::string toString()
Retrieve a string representation of the TCPSocket.
Definition: tcpsocket.cc:128
int send(const void *const buf, int len, int flags)
Definition: tcpsocket.cc:363
bool bindSocket(const char *const Host, int Port)
Binds to a local address and port.
Definition: tcpsocket.cc:388
bool m_Bound
Definition: tcpsocket.h:232
TCPSocket * accept(std::string *Peer)
Accept a connection; blocking, non-cancellable.
Definition: tcpsocket.cc:208
struct sockaddr m_PeerAddr
Definition: tcpsocket.h:229
int recv(void *buf, int len, int flags)
Definition: tcpsocket.cc:352
bool dataToGet(int sec, int usec) const
Check and optionally wait for incoming data.
Definition: tcpsocket.cc:277
virtual ~TCPSocket()
Destructor.
Definition: tcpsocket.cc:78
virtual bool connect(const char *const Peer, int PeerPort, const char *const Host=NULL, int HostPort=0)
Connects to a given host.
Definition: tcpsocket.cc:153
A generic container for an array of bytes.
Definition: bufferstore.h:37
void addBuff(const bufferStore &b, long maxLen=-1)
Appends data to the content of this instance.
Definition: bufferstore.cc:182
void addBytes(const unsigned char *buf, int len)
Appends data to the content of this instance.
Definition: bufferstore.cc:176
const char * getString(long pos=0) const
Retrieves the characters at index pos.
Definition: bufferstore.cc:120
Definition: doctest.h:522
static rfsv * a
Definition: main.cc:53
#define INVALID_SOCKET
Definition: tcpsocket.cc:43
#define SOCKET_ERROR
Definition: tcpsocket.cc:44
#define MSG_NOSIGNAL
Definition: tcpsocket.cc:47