|
Chi dispone di una linea ADSL a casa propria nella
maggiorparte dei casi non dispone di un indirizzo IP fisso ma l' indirizzo IP
della connessione internet viene generato dinamicamente tutte le volte che la
connessione viene instaurata. Se ho un computer su cui lavora ad esempio mia
sorella che usa internet e quando sono in giro col mio portatile voglio
collegarmi a questo computer, devo sapere ogni volta l' indirizzo IP che è stato
assegnato. Spulciano nell' MSDN ho trovato l' API Win32
NotifyAddrChange che consente la rilevazione di variazioni sugli indirizzi
delle varie interfacce di rete: è su questa api che ho costruito tutto
l'applicativo
1. NotifyAddrChange
L'api win32 NotifyAddrChange consente di rilevare variazioni agli
indirizzi IP delle interfacce di rete presenti. Trattandosi di una procedura
bloccante che rimane in attesa dell'evento (ossia la variazione di un indirizzo
IP) è opportuno chiamare l'api in questione all'interno di un thread.
1.1 Procedura per rilevare le variazioni sugli indirizzi IP
Si
perviene al seguente codice che può essere considerato il kernel del nostro
applicativo
type
TNotifyIpChangeThread = class(TThread)
public
procedure Execute; override;
end;
...
procedure TNotifyIpChangeThread.Execute;
var
overlap: OVERLAPPED;
hand: THandle;
ret: DWORD;
Closed: Boolean;
begin
while not Terminated do
begin
hand := WSACreateEvent();
overlap.hEvent := WSACreateEvent();
ret := NotifyAddrChange(hand, @overlap);
if (ret <> NO_ERROR) then
begin
if (WSAGetLastError() <> 997) then //997 --> WSA_IO_PENDING
begin
//qui si può segnalare che c'è stato un errore,
//ad esempio salvare in un file di log
exit;
end;
end;
Closed := False;
repeat
case WaitForSingleObject(overlap.hEvent, 1000) of
WAIT_OBJECT_0 :
begin
//qui si può chiamare una procedura
//creata da noi che va a calcolare gli indirizzi IP
//e poi eseguire altre operazione come ad esempio
//inviare gli IP via email, etc...
Closed := True;
end;
WAIT_FAILED : RaiseLastWin32Error;
end;
until (Closed or Terminated);
end;
end;
di seguito la le dichiarazioni e le definizioni delle funzioni e tipi usati nel codice appena esposto
const
WSA_IO_PENDING = 997;
type
POVERLAPPED = ^OVERLAPPED;
OVERLAPPED = record
Internal: Cardinal;
InternalHigh: Cardinal;
Union: record
case Integer of
0: (
Offset: Cardinal;
OffsetHigh: Cardinal);
1: (
Ptr: Pointer);
end;
hEvent: Cardinal;
end;
function NotifyAddrChange(
var Handle: Cardinal;
overlapped: POVERLAPPED): Cardinal; stdcall; external 'iphlpapi.dll' name 'NotifyAddrChange';
function WSACreateEvent: Cardinal; stdcall; external 'ws2_32.dll' name 'WSACreateEvent';
function WSAGetLastError: Integer; stdcall; external 'ws2_32.dll' name 'WSAGetLastError';
2. Funzione per ottenere l'elenco degli indirizzi IP relativamente a tutte le interfacce di rete Dopo aver rilevato una variazione negli indirizzi IP è d'obbligo chiamare una funzione che mi restituisca i nuovi indirizzi IP.
//Convert IP address to dotted decimal
function IpAddrToString(Addr: Cardinal): string;
var
inad: in_addr;
begin
inad.s_addr := Addr;
Result := inet_ntoa(inad); //dichiarata nella Unit Winsock
end;
function ElencoIndirizzi: string;
var
IpAddrTable: PMIB_IPADDRTABLE;
i: integer;
Indirizzi: string;
NroAdapters: integer;
begin
IpAddrTable := GetIpAddrTableWithAlloc;
Indirizzi := '';
NroAdapters := IpAddrTable^.dwNumEntries;
//scrivo l' indirizzo IP di tutte le interfacce di rete
for i := 0 to IpAddrTable^.dwNumEntries - 1 do
begin
Indirizzi := Indirizzi + IpAddrToString(IpAddrTable^.table[i].dwAddr) + #13#10;
end;
end;
di seguito la le dichiarazioni e le definizioni delle funzioni e tipi usati nel codice appena esposto
type
PMIB_IPADDRROW = ^MIB_IPADDRROW;
MIB_IPADDRROW = record
dwAddr: Cardinal;
dwIndex: Cardinal;
dwMask: Cardinal;
dwBCastAddr: Cardinal;
dwReasmSize: Cardinal;
unused1: Word;
wType: Word;
end;
PMIB_IPADDRTABLE = ^MIB_IPADDRTABLE;
MIB_IPADDRTABLE = record
dwNumEntries: Cardinal;
table: array [0..0] of MIB_IPADDRROW;
end;
function GetIpAddrTable(
pIpAddrTable: PMIB_IPADDRTABLE;
var pdwSize: Cardinal;
bOrder: Boolean): Cardinal; stdcall; external 'iphlpapi.dll' name 'GetIpAddrTable';
3. Invio di email Una volta rilevata una variazione negli indirizzi IP e raccolti i nuovi indirizzi IP, un'ottima idea può essere quella di inviare gli IP ad un indirizzo email. L'implementazione di tale procedura di invio email può essere effettuata molto semplicemente grazie agli Indy Components. Useremo i seguenti 2 componenti appartenenti al pacchetto Indy: 1) TIdMessage: raccoglie tutte le caratteristiche del messaggio (indirizzo destinazione, body, etc...) 2) TIdSMTP: si occupa dell'invio 3.1 Procedura per inviare email Creiamo un semplice applicativo: sulla nostra Form mettiamo i 2 suddetti componenti e via
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdMessage, IdBaseComponent, IdComponent, IdTCPConnection,
IdTCPClient, IdMessageClient, IdSMTP;
type
TForm1 = class(TForm)
SMTP: TIdSMTP;
MailMessage: TIdMessage;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure Invioemail;
begin
Form1.MailMessage.From.Address := 'indirizzo email from';
Form1.MailMessage.Recipients.EMailAddresses := 'indirizzo email destinazione';
Form1.MailMessage.Subject := 'test invio posta';
Form1.MailMessage.Body.Text := 'testo del messaggio';
//send mail
try
try
Form1.SMTP.Connect(1000);
Form1.SMTP.Send(svcIpNotify.MailMessage);
except on E:Exception do
//StatusMemo.Lines.Insert(0, 'ERROR: ' + E.Message);
end;
finally
if Form1.SMTP.Connected then
Form1.SMTP.Disconnect;
end;
end;
end.
4. Creare un servizio che ingloba il tutto A questo punto, la cosa migliore è creare un servizio in maniera tale che la procedura di rilevazione delle variazioni (che fa capo all'api NotifyAddrChange) sia sempre attiva. Tralascerei l'esposizione del sorgente del servizio e mi soffermerei sulle caratteristiche salienti del servizio stesso. 4.1 Caratteristiche del servizio L' eseguibile che implementa il servizio si chiama Carlo_ip.exe mentre il nome visualizzato del servizio (la stringa che viene visualizzata nell' elenco dei servizi restituito dall' applet services.msc) è IpNotify. Il nome del servizio (nome della sottochiave della chiave di registro HKLM\System\CurrentControlSet\Services) è svcIpNotify. Bisogna inserire nel codice il "from" ed il "to" relativamente alla email; una volta creato l' eseguibile si procede alla sua installazione tramite
Carlo_ip.exe /INSTALL
A questo punto eseguendo services.msc ci sarà nell' elenco la voce IpNotify. Aprendo il registro ci sarà la chiave HKLM\System\CurrentControlSet\Services\svcIpNotify.
5. Conclusione Tutte le volte che ci si connetterà ad internet (o verrà eseguita una qualsiasi altra modifica ad una qualsiasi interfaccia di rete) verrà inviata una email con l' indirizzo IP di tutte le interfacce di rete e con un soggetto del tipo: "notifica <data ora>" IPNotify
|