program logresolvalx; (* * Alexandre Alapetite https://alexandre.alapetite.fr * * 2004-02-01 : version 1.0 for Borland Delphi 7.0 Win32 * * === about === * # This program is an alternative to the official logresolve utility provided with Apache Web server. * http://httpd.apache.org/docs/programs/logresolve.html * # So it converts IP adresses to DNS in a CLF Web server log file * # It is multi-threaded and can deal with zero-length DNS. * * === usage === * # logresolvalx.exe log.txt logdns.txt * * === credits === * # This program is freeware and open source by Alexandre Alapetite https://alexandre.alapetite.fr/cv/ * You can use the source code and the binary program * as long as you keep the credits in the source code and for the final user. * Copyright 2004, Licence: Creative Commons "Attribution-ShareAlike 2.0 France" BY-SA (FR), * http://creativecommons.org/licenses/by-sa/2.0/fr/ * https://alexandre.alapetite.fr/divers/apropos/#by-sa * - Attribution. You must give the original author credit * - Share Alike. If you alter, transform, or build upon this work, * you may distribute the resulting work only under a license identical to this one * - The French law is authoritative * - Any of these conditions can be waived if you get permission from Alexandre Alapetite * - Please send to Alexandre Alapetite the modifications you make, * in order to improve this file for the benefit of everybody * If you want to distribute this code, please do it as a link to: * https://alexandre.alapetite.fr/download/logresolve-alx/ * * # GetHostByAddr() by Ernesto De Spirito http://www.latiumsoftware.com/en/pascal/0038.php *) {$APPTYPE CONSOLE} {$OPTIMIZATION ON} {$TYPEDADDRESS ON} {$RANGECHECKS ON} {$IOCHECKS ON} {$OVERFLOWCHECKS ON} uses Classes, SysUtils, Math, WinSock; type T1IP2dns=class(TThread) private fIPs:TStrings; fID:Integer; fNbThreads:PInteger; protected procedure Execute(); override; function ip2dns(const IP:String):String; public constructor Create(aIPs:TStrings; const aId:Integer; aNbThreads:PInteger); end; const MAX_THREADS=30; var log,ips:TStringList; s:String; i,j,nbThreads,nbIP:Integer; procedure help(); begin Writeln; Writeln(' +--------------------------------+'); Writeln(' | LogResolve alx version 1.0 |'); Writeln(' | https://alexandre.alapetite.fr |'); Writeln(' +--------------------------------+'); Writeln; Writeln('IP -> DNS in a CLF Web server log file'); Writeln; Writeln('Usage: logresolvalx '); Writeln; end; procedure resolve(aIPs:TStrings); const TIME_OUT=100; MAX_LOOPS=(60*1000) div TIME_OUT; var i,m,n:Integer; v:Boolean; begin nbThreads:=0; nbIP:=0; n:=0; v:=False; with aIPs do while nbIP0) and (m>0) do begin Dec(m); if n<>nbThreads then begin n:=nbThreads; Writeln('0 ('+IntToStr(n)+')'); end; Sleep(TIME_OUT); end; end; procedure logdns(aLog:TStrings; aIPs:TStrings); var i,j:Integer; s,t:String; begin with aLog do for i:=0 to Pred(Count) do begin s:=Strings[i]; j:=Pos(' ',s); t:=Copy(s,j,MaxInt); s:=Copy(s,1,Pred(j)); s:=aIPs.Values[s]; Strings[i]:=s+t; end; end; { ------------- T1IP2dns ------------- } constructor T1IP2dns.Create(aIPs:TStrings; const aId:Integer; aNbThreads:PInteger); begin inherited Create(False); Priority:=tpLower; FreeOnTerminate:=True; fIPs:=aIPs; fID:=aId; fNbThreads:=aNbThreads; Resume(); end; procedure T1IP2dns.Execute(); var s,dns:String; begin try with fIPs do try dns:=''; s:=ValueFromIndex[fID]; dns:=ip2dns(s); if Length(dns)>1 then ValueFromIndex[fID]:=dns; except on E:Exception do Writeln(s+' /!\ '+E.Message); end; finally Dec(fNbThreads^); end; end; function T1IP2dns.ip2dns(const IP:String):String; var WSData:TWSAData; SockAddrIn:TSockAddrIn; HostEnt:PHostEnt; begin //By: Ernesto De Spirito //Source: http://www.latiumsoftware.com/en/pascal/0038.php Result:=IP; if WSAStartup($101,WSData)=0 then //Try to start a winsocks session begin SockAddrIn.sin_addr.S_addr:=inet_addr(PChar(IP)); //IP String=>Integer (network byte order) HostEnt:=WinSock.GetHostByAddr( @SockAddrIn.sin_addr.S_addr, SizeOf(SockAddrIn.sin_addr.S_addr),AF_INET); if HostEnt<>nil then Result:=StrPas(Hostent^.h_name); WSACleanup(); //Close the winsocks session end; end; { ------------- main ------------- } begin if ParamCount>0 then begin s:=ParamStr(1); if s[1]='<' then s:=Copy(s,2,MaxInt); if s[Length(s)]='>' then s:=Copy(s,1,Length(s)-1); if Pos('?',s)>0 then help() else if FileExists(s) then begin log:=nil; ips:=nil; try log:=TStringList.Create(); try log.LoadFromFile(s); ips:=TStringList.Create(); ips.Duplicates:=dupIgnore; with log do for i:=0 to Pred(Count) do begin s:=Strings[i]; j:=Pos(' ',s); s:=Copy(s,1,Pred(j)); ips.Values[s]:=s; end; resolve(ips); logdns(log,ips); if ParamCount>1 then begin s:=ParamStr(2); log.SaveToFile(s); end else with log do for i:=0 to Pred(Count) do Writeln(Strings[i]); except on E:Exception do Writeln(E.Message); end; finally ips.Free(); log.Free(); end; end else begin Writeln('Invalid file name for input log'); Writeln; end; end else help(); end.