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
unit Common;
interface
-uses Windows, SysUtils, DateUtils, Classes, INIFiles;
+uses Windows, SysUtils, DateUtils, Classes, INIFiles, RFCSMTP;
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;
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;
end;
end;
+procedure ParseIPv6Address(S: string; var Address: string; var Port: word);
+{ IPv6 addresses can be supplied in the following formats:
+ [<IPv6 address>]:<port> e.g. [::1]:25
+ or
+ <hostname>:<port> e.g. mail.example.com:25 }
+var SPort: string; c: integer;
+begin
+ if S[1] = '[' then begin
+ { Guess format is "[<IPv6 address>]:<port>". }
+ 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 "<hostname>:<port>". }
+ 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
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);
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;
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
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);
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;
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;
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
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;
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;