2 Basic object-oriented network functions
3 Copyright (C) 2010-2018 MegaBrutal
5 This unit is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This unit is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 This unit provides an object-oriented interface to manage TCP/IPv4
24 TTCPConnection - provides methods for sending/receiving buffers through
25 the connection, and some support for text-based communication.
27 TTCPRFCConnection - in addition to TTCPConnection, it provides methods
28 to send/receive RFC-style commands and responses.
30 TTCPListener - opens a port to listen on, and accepts incoming connections
31 through it. Override its "HandleClient" method to serve connected
40 uses Classes
, Sockets
, SocketUtils
, DNSResolve
, NetRFC
, Common
;
44 { Connection feature requests: }
46 NET_TCP_RFCSUPPORT
= 1;
48 { Default socket timeout: }
49 DEF_SOCK_TIMEOUT
= 5 * 60000; { 5 minutes. }
54 TTCPConnection
= class
55 constructor Create
; overload
;
56 constructor Create(const HostName
: string; Port
: word); overload
;
57 constructor Create(Socket
: socket
; const Addr
: TSockAddr
); overload
;
58 destructor Destroy
; override;
66 function Connect(const HostName
: string; Port
: word): boolean;
68 procedure ReverseDNSLookup
;
69 function VerifyFCrDNS
: boolean;
70 procedure SetSockTimeOut(TimeOut
: DWord
);
71 function ReadBuffer(PtrBuffer
: pointer; Len
: size_t
): ssize_t
;
72 function WriteBuffer(PtrBuffer
: pointer; Len
: size_t
): ssize_t
;
73 function ReadLn(var Line
: string): boolean;
74 function WriteLn(const Line
: string): boolean;
75 property Connected
: boolean read FConnected
;
76 property Socket
: socket read FSocket
;
77 property HostIP
: TIPNamePair read FHostIP
;
78 property SockTimeOut
: DWord read FSockTimeOut write SetSockTimeOut
;
81 TTCPRFCConnection
= class(TTCPConnection
)
83 function ReadCommand(var Command
: shortstring
; var Prms
: string): boolean;
84 function ReadResponse(Response
: TRFCReply
): boolean;
85 function SendCommand(Command
: shortstring
): boolean; overload
;
86 function SendCommand(Command
: shortstring
; Prms
: string): boolean; overload
;
87 function SendResponse(Response
: TRFCReply
): boolean;
91 TTCPAcceptHandler
= procedure(Connection
: TTCPConnection
) of object;
93 TTCPAcceptor
= class(TThread
)
94 constructor Create(Handler
: TTCPAcceptHandler
; TCPConnection
: TTCPConnection
);
96 FHandler
: TTCPAcceptHandler
;
97 FTCPConnection
: TTCPConnection
;
98 procedure Execute
; override;
101 TTCPListener
= class(TThread
)
102 constructor Create(const Address
: string; Port
: word; FeatureRequest
: word);
103 {destructor Destroy; override;}
105 FFeatureRequest
: word;
106 FListenAddress
: string;
108 FListenSocket
: socket
;
111 procedure HandleClient(Connection
: TTCPConnection
); virtual; abstract;
112 procedure Execute
; override;
114 property ListenPort
: word read FListenPort
;
115 function StartListen
: boolean;
116 procedure StopListen
;
124 constructor TTCPConnection
.Create
;
125 { Create an instance, but don't connect to anywhere yet. }
130 FSockTimeOut
:= DEF_SOCK_TIMEOUT
;
133 constructor TTCPConnection
.Create(const HostName
: string; Port
: word);
134 { Connect to the given port on the given hostname. }
137 Connect(HostName
, Port
);
140 constructor TTCPConnection
.Create(Socket
: socket
; const Addr
: TSockAddr
);
141 { Use an already connected socket. }
146 FHostIP
:= TIPNamePair
.Create('', NetAddrToStr(Addr
.sin_addr
));
150 destructor TTCPConnection
.Destroy
;
152 if FConnected
then Disconnect
;
157 constructor TTCPAcceptor
.Create(Handler
: TTCPAcceptHandler
; TCPConnection
: TTCPConnection
);
158 { Start a connection handler on a distinct thread. }
161 FTCPConnection
:= TCPConnection
;
162 FreeOnTerminate
:= true;
163 inherited Create(false);
167 constructor TTCPListener
.Create(const Address
: string; Port
: word; FeatureRequest
: word);
169 FListenAddress
:= Address
;
171 FFeatureRequest
:= FeatureRequest
;
172 FreeOnTerminate
:= false;
173 inherited Create(true);
177 function TTCPConnection
.Connect(const HostName
: string; Port
: word): boolean;
178 { Resolves the given hostname, and tries to connect it on the given port. }
180 FSocket
:= fpSocket(af_inet
, sock_stream
, 0);
181 if (FSocket
<> -1) then begin
183 sin_family
:= af_inet
;
184 sin_port
:= htons(Port
);
185 { Resolve hostname to IP address. }
186 sin_addr
:= ResolveHost(HostName
);
189 if sAddr
.sin_addr
.s_addr
<> 0 then
190 { Try to initiate connection. }
191 FConnected
:= fpConnect(FSocket
, @sAddr
, SizeOf(sAddr
)) <> -1;
193 if FConnected
then begin
194 FHostIP
:= TIPNamePair
.Create(HostName
, NetAddrToStr(sAddr
.sin_addr
));
195 SetSockTimeOut(FSockTimeOut
);
198 CloseSocket(FSocket
);
203 procedure TTCPConnection
.Disconnect
;
205 fpShutdown(FSocket
, 2);
206 CloseSocket(FSocket
);
212 procedure TTCPConnection
.ReverseDNSLookup
;
213 { Performs a reverse DNS lookup, and updates the HostIP structure. }
214 var NHostIP
: TIPNamePair
;
216 if FConnected
then begin
217 NHostIP
:= TIPNamePair
.Create(ResolveIP(sAddr
.sin_addr
), FHostIP
.IP
);
223 function TTCPConnection
.VerifyFCrDNS
: boolean;
225 Result
:= NetAddrToStr(ResolveHost(HostIP
.Name
)) = HostIP
.IP
;
228 procedure TTCPConnection
.SetSockTimeOut(TimeOut
: DWord
);
230 FSockTimeOut
:= TimeOut
;
231 if Connected
then begin
232 fpSetSockOpt(FSocket
, SOL_SOCKET
, SO_RCVTIMEO
, @FSockTimeOut
, SizeOf(FSockTimeOut
));
233 fpSetSockOpt(FSocket
, SOL_SOCKET
, SO_SNDTIMEO
, @FSockTimeOut
, SizeOf(FSockTimeOut
));
237 function TTCPConnection
.ReadBuffer(PtrBuffer
: pointer; Len
: size_t
): ssize_t
;
239 Result
:= fpRecv(FSocket
, PtrBuffer
, Len
, 0);
242 function TTCPConnection
.WriteBuffer(PtrBuffer
: pointer; Len
: size_t
): ssize_t
;
244 Result
:= fpSend(FSocket
, PtrBuffer
, Len
, 0);
247 function TTCPConnection
.ReadLn(var Line
: string): boolean;
249 Result
:= SockReadLn(FSocket
, Line
);
252 function TTCPConnection
.WriteLn(const Line
: string): boolean;
254 Result
:= SockWriteLn(FSocket
, Line
);
258 function TTCPRFCConnection
.ReadCommand(var Command
: shortstring
; var Prms
: string): boolean;
260 Result
:= NetRFC
.ReadCommand(FSocket
, Command
, Prms
);
263 function TTCPRFCConnection
.ReadResponse(Response
: TRFCReply
): boolean;
265 Result
:= NetRFC
.ReadResponse(FSocket
, Response
);
268 function TTCPRFCConnection
.SendCommand(Command
: shortstring
): boolean;
270 Result
:= NetRFC
.SendCommand(FSocket
, Command
);
273 function TTCPRFCConnection
.SendCommand(Command
: shortstring
; Prms
: string): boolean;
275 Result
:= NetRFC
.SendCommand(FSocket
, Command
, Prms
);
278 function TTCPRFCConnection
.SendResponse(Response
: TRFCReply
): boolean;
280 Result
:= NetRFC
.SendResponse(FSocket
, Response
);
284 procedure TTCPAcceptor
.Execute
;
286 FHandler(FTCPConnection
);
290 function TTCPListener
.StartListen
: boolean;
292 FListenSocket
:= fpSocket(af_inet
, sock_stream
, 0);
293 if FListenSocket
<> -1 then begin
295 sin_family
:= af_inet
;
296 sin_port
:= htons(FListenPort
);
297 sin_addr
:= ResolveHost(FListenAddress
);
299 if fpBind(FListenSocket
, @sAddr
, sizeof(sAddr
)) <> -1 then begin
300 { It seems the maximum connection value isn't enforced by the
301 Free Pascal library, so this 512 is a constant, dummy value. }
302 if fpListen(FListenSocket
, 512) <> -1 then begin
313 procedure TTCPListener
.StopListen
;
319 procedure TTCPListener
.Execute
;
320 var ClientSocket
: socket
; AcceptFailCount
: word; Len
: longint;
321 TCPConnection
: TTCPConnection
;
323 { Now, accept connections. }
325 while not Terminated
do begin
327 ClientSocket
:= fpAccept(FListenSocket
, @sAddr
, @Len
);
328 if ClientSocket
<> -1 then begin
331 { Creates the requested TTCPConnection object for the accepted
333 case FFeatureRequest
of
335 TCPConnection
:= TTCPConnection
.Create(ClientSocket
, sAddr
);
337 TCPConnection
:= TTCPRFCConnection
.Create(ClientSocket
, sAddr
);
340 { Then start a new thread with the connection handler. }
341 TTCPAcceptor
.Create(HandleClient
, TCPConnection
);
344 Inc(AcceptFailCount
);
345 if AcceptFailCount
>= 512 then Terminate
;