From: MegaBrutal Date: Wed, 7 Nov 2018 06:25:00 +0000 (+0100) Subject: Enable IPv6 listen address (ListenAddress6) X-Git-Url: http://git.megabrutal.com/?p=mgsmtp.git;a=commitdiff_plain;h=973fc2e5ed9372c813ce98f65ad4496c0935185b Enable IPv6 listen address (ListenAddress6) ListenAddress6 can be used to specify IPv6 addresses to listen on. Hostnames are resolved, numeric IPv6 addresses must be put in brackets. modified: Common.pas modified: DNSResolve.pas modified: Listener.pas modified: MgSMTP.pas modified: Network.pas --- diff --git a/Common.pas b/Common.pas index fadccc5..2280c2b 100644 --- a/Common.pas +++ b/Common.pas @@ -26,7 +26,7 @@ unit Common; interface -uses Windows, SysUtils, DateUtils, Classes, INIFiles; +uses Windows, SysUtils, DateUtils, Classes, INIFiles, RFCSMTP; type @@ -73,10 +73,11 @@ type {FTimeCorrection: integer;} FTimeOffset: integer; FTimeOffsetStr: string; - FListenAddresses: TStrings; + FListenAddresses, FListenAddresses6: TStrings; public function GetVersionStr: string; property ListenAddresses: TStrings read FListenAddresses; + property ListenAddresses6: TStrings read FListenAddresses6; property Databytes: longint read FDatabytes; {property TimeCorrection: integer read FTimeCorrection;} property TimeOffset: integer read FTimeOffset; @@ -165,6 +166,7 @@ type function IsPrintableString(S: string): boolean; function UnixTimeStamp(DateTime: TDateTime): TUnixTimeStamp; function CmdlineToStringArray: TStringArray; + procedure ParseIPv6Address(S: string; var Address: string; var Port: word); procedure SplitParameters(S: string; var FirstPrm, Remainder: string; Separator: char = #32); function ReadLineFromStream(Stream: TStream): string; @@ -385,6 +387,54 @@ begin end; end; +procedure ParseIPv6Address(S: string; var Address: string; var Port: word); +{ IPv6 addresses can be supplied in the following formats: + []: e.g. [::1]:25 + or + : e.g. mail.example.com:25 } +var SPort: string; c: integer; +begin + if S[1] = '[' then begin + { Guess format is "[]:". } + c:= pos(']', S); + if c > 1 then begin + Address:= Copy(S, 2, c - 2); + if c = Length(S) then begin + { There is no port to extract. } + Port:= STANDARD_SMTP_PORT; + end + else begin + { The closing bracket should be followed by a colon. } + if S[c+1] = ':' then + { Extract port number. } + Port:= StrToIntDef(Copy(S, c+2, Length(S) - (c+1)), 0) + else + { Invalid format. } + Port:= 0; + end; + end + else begin + { Format is incorrect, return invalid data. } + Address:= ''; + Port:= 0; + end; + end + else begin + { Guess format is ":". } + SplitParameters(S, Address, SPort, ':'); + if (pos(':', Address) = 0) and (pos(':', SPort) = 0) then begin + { Format seems correct. } + if SPort = '' then Port:= STANDARD_SMTP_PORT + else Port:= StrToIntDef(SPort, 0); + end + else begin + { Format is incorrect, return invalid data. } + Address:= ''; + Port:= 0; + end; + end; +end; + procedure SplitParameters(S: string; var FirstPrm, Remainder: string; Separator: char = #32); var i: integer; begin @@ -483,6 +533,10 @@ begin portlist.Free; end; + FListenAddresses6:= TStringList.Create; + FListenAddresses6.Delimiter:= ','; + FListenAddresses6.DelimitedText:= Config.ReadString('Server', 'ListenAddress6', ''); + FDatabytes:= Config.ReadInteger('Server', 'Databytes', 1024 * 1024 * 1024); FTimeOffset:= Config.ReadInteger('Server', 'TimeOffset', Config.ReadInteger('Server', 'TimeCorrection', 0) * 100); FTimeOffsetStr:= MakeTimeOffsetStr(FTimeOffset); diff --git a/DNSResolve.pas b/DNSResolve.pas index fb4b467..27913b0 100644 --- a/DNSResolve.pas +++ b/DNSResolve.pas @@ -55,7 +55,8 @@ type function ResolveHost(HostName: ansistring; Family: cint): TGAIResult; procedure FreeHost(var GAIResult: TGAIResult); - function ResolveIP(AddrInfo: PAddrInfo): ansistring; + function ResolveIP(AddrInfo: PAddrInfo): ansistring; overload; + function ResolveIP(SockAddr: PSockAddr): ansistring; overload; function IPToStr(SockAddr: PSockAddr): ansistring; @@ -93,6 +94,22 @@ begin else ResolveIP:= IPToStr(AddrInfo^.ai_addr); end; +function ResolveIP(SockAddr: PSockAddr): ansistring; +var + AddrInfo: TAddrInfo; +begin + AddrInfo.ai_addr:= SockAddr; + + if SockAddr^.sa_family = AF_INET then + AddrInfo.ai_addrlen:= SizeOf(TSockAddr) + else if SockAddr^.sa_family = AF_INET6 then + AddrInfo.ai_addrlen:= SizeOf(TSockAddr6) + else + AddrInfo.ai_addrlen:= 0; + + ResolveIP:= ResolveIP(PAddrInfo(@AddrInfo)); +end; + function IPToStr(SockAddr: PSockAddr): ansistring; begin if SockAddr^.sa_family = AF_INET then diff --git a/Listener.pas b/Listener.pas index fc92edd..bff8dcf 100644 --- a/Listener.pas +++ b/Listener.pas @@ -38,7 +38,7 @@ uses SysUtils, Classes, Base64, Network, NetRFC, RFCSMTP, type TMgSMTPListener = class(TTCPListener) - constructor Create(const Address: string; Port: word); + constructor Create(const Address: string; Port, Family: word); protected procedure HandleClient(Connection: TTCPConnection); override; procedure ReceiveEMailData(TCP: TTCPRFCConnection; Response: TRFCReply; SpoolObject: TSpoolObjectCreator); @@ -57,14 +57,20 @@ var procedure StartListeners; -var i: integer; address, port: string; +var i, j: integer; address, port: string; nport: word; begin - SetLength(MgSMTPListeners, MainServerConfig.ListenAddresses.Count); - for i:= 0 to Length(MgSMTPListeners) - 1 do begin + SetLength(MgSMTPListeners, MainServerConfig.ListenAddresses.Count + MainServerConfig.ListenAddresses6.Count); + for i:= 0 to MainServerConfig.ListenAddresses.Count - 1 do begin SplitParameters(MainServerConfig.ListenAddresses.Strings[i], address, port, ':'); - MgSMTPListeners[i]:= TMgSMTPListener.Create(address, StrToIntDef(port, STANDARD_SMTP_PORT)); + MgSMTPListeners[i]:= TMgSMTPListener.Create(address, StrToIntDef(port, STANDARD_SMTP_PORT), AF_INET); MgSMTPListeners[i].StartListen; end; + j:= MainServerConfig.ListenAddresses.Count; + for i:= 0 to MainServerConfig.ListenAddresses6.Count - 1 do begin + ParseIPv6Address(MainServerConfig.ListenAddresses6.Strings[i], address, nport); + MgSMTPListeners[j+i]:= TMgSMTPListener.Create(address, nport, AF_INET6); + MgSMTPListeners[j+i].StartListen; + end; end; procedure StopListeners; @@ -128,10 +134,10 @@ begin end; -constructor TMgSMTPListener.Create(const Address: string; Port: word); +constructor TMgSMTPListener.Create(const Address: string; Port, Family: word); begin { Request connection objects with support for RFC-style commands & responses. } - inherited Create(Address, Port, NET_TCP_RFCSUPPORT); + inherited Create(Address, Port, Family, NET_TCP_RFCSUPPORT); Logger.AddLine('Server', 'Listening on address: ' + Address + ':' + IntToStr(Port)); end; diff --git a/MgSMTP.pas b/MgSMTP.pas index a9c51e7..154ad63 100644 --- a/MgSMTP.pas +++ b/MgSMTP.pas @@ -49,7 +49,7 @@ const document what bugfix/feature are you testing with the actual build. This will be logged to help you differentiate outputs of subsequent builds in your logs. If left empty, it won't be added to the logs. } - DEVCOMMENT = 'Use getaddrinfo & getnameinfo'; + DEVCOMMENT = 'IPv6 listen addresses (ListenAddress6)'; var diff --git a/Network.pas b/Network.pas index b19dd7d..7521ff1 100644 --- a/Network.pas +++ b/Network.pas @@ -41,6 +41,13 @@ uses Classes, Sockets, SocketUtils, DNSResolve, NetRFC, Common; const + { Address families: } + { These are here so users of this unit don't necessarily have to + use Sockets as well. } + AF_UNSPEC = Sockets.AF_UNSPEC; + AF_INET = Sockets.AF_INET; + AF_INET6 = Sockets.AF_INET6; + { Connection feature requests: } NET_TCP_BASIC = 0; NET_TCP_RFCSUPPORT = 1; @@ -218,7 +225,7 @@ procedure TTCPConnection.ReverseDNSLookup; var NHostIP: TIPNamePair; begin if FConnected then begin - NHostIP:= TIPNamePair.Create(ResolveIP(@SockAddr), FHostIP.IP); + NHostIP:= TIPNamePair.Create(ResolveIP(PSockAddr(@SockAddr)), FHostIP.IP); FHostIP.Free; FHostIP:= NHostIP; end;