uproszczony klient POP3 - Instytut Informatyki Teoretycznej i

Transkrypt

uproszczony klient POP3 - Instytut Informatyki Teoretycznej i
Architektura typu klient – serwer:
uproszczony klient POP3
Wydział Inżynierii Mechanicznej i Informatyki
Instytut Informatyki Teoretycznej i Stosowanej
dr inż. Łukasz Szustak
Składniki systemu poczty e-mail POP3
●
●
System poczty e-mail POP3 składa się z trzech składników:
klienta poczty e-mail POP3, usługi SMTP (Simple Mail Transfer
Protocol) oraz usługi POP3
Klient poczty e-mail POP3:
–
Oprogramowanie klienta poczty e-mail POP3 służy do
czytania oraz redagowania wiadomości e-mail
–
Klient poczty e-mail POP3 pobiera pocztę e-mail z serwera
poczty i dokonuje jej transferu na komputer lokalny, aby
użytkownik mógł nią zarządzać
–
Klient poczty e-mail POP3 bazuje na protokole POP3
Składniki systemu poczty e-mail POP3
●
Usługa SMTP:
–
System transferu poczty e-mail, który kieruje wiadomość
e-mail od nadawcy do adresata przy użyciu protokołu SMTP
–
Usługa POP3 używa usługi SMTP jako systemu transferu
poczty e-mail
–
Wiadomość e-mail jest redagowana przez użytkownika w
oprogramowaniu klienta poczty e-mail POP3
–
Następnie, gdy użytkownik połączy się z serwerem poczty za
pośrednictwem Internetu lub połączenia sieciowego, usługa
SMTP zabiera wiadomość e-mail i transferuje ją Internetem
na serwer poczty adresata
Składniki systemu poczty e-mail POP3
●
Usługa POP3:
–
System pobierania poczty e-mail, który pobiera pocztę e-mail
z serwera poczty na lokalny komputer użytkownika przy
użyciu protokołu POP3
–
Protokół POP3 kontroluje połączenie między klientem poczty
e-mail POP3 użytkownika a serwerem, na którym poczta
e-mail jest przechowywana
System poczty e-mail POP3
System poczty e-mail POP3
●
●
●
●
●
Krok 1: wiadomość e-mail zostaje wysłana pod adres
osoba@przykład.com
Krok 2: wiadomość e-mail zostaje pobrana przez usługę SMTP i
wysłana przez Internet
Krok 3: domena poczty e-mail, przykład.com, zostaje rozpoznana jako
serwer poczty w Internecie, mailserver1.przykład.com
(mailserver1.przykład.com jest serwerem poczty, na którym jest
uruchomiona usługa POP3 i który odbiera przychodzącą pocztę
e-mail dla domeny poczty e-mail przykład.com)
Krok 4: wiadomość e-mail wysłana pod adres osoba@przykład.com
zostaje odebrana przez serwer poczty mailserver1.przykład.com
Krok 5: wiadomość została przeniesiona do katalogu magazynu
poczty, w którym jest przechowywana poczta e-mail kierowana pod
adres osoba@przykład.com
System poczty e-mail POP3
●
●
Krok 6: użytkownik someone łączy się z serwerem poczty, na którym
jest uruchomiona usługa POP3, aby sprawdzić, czy nie przyszła nowa
poczta e-mail: protokół POP3 wysyła poświadczenia uwierzytelnienia
użytkownika, czyli hasło i nazwę użytkownika someone, następnie
usługa POP3 weryfikuje te poświadczenia, a następnie akceptuje lub
odrzuca połączenie
Krok 7: jeśli połączenie zostaje zaakceptowane, cała poczta e-mail dla
użytkownika someone, która jest przechowywana w magazynie
poczty, zostaje pobrana/przeniesiona z serwera poczty na komputer
lokalny użytkownika someone
Protokół POP3
●
●
●
●
Protokół POP3 (Post Office Protocol 3) jest standardowym
protokołem pobierania poczty e-mail
Protokół POP3 kontroluje połączenie między klientem poczty
e-mail POP3 a serwerem, na którym poczta e-mail jest
przechowywana
Usługa POP3 wykorzystuje protokół POP3 do pobierania poczty
e-mail z serwera
Protokół POP3 ma trzy stany przetwarzania związane z obsługą
połączenia między serwerem poczty a klientem poczty e-mail
POP3:
–
stan uwierzytelniania
–
stan transakcji
–
stan aktualizacji
Protokół POP3
●
●
●
●
W stanie uwierzytelniania klient poczty e-mail POP3, który
łączy się z serwerem, musi zostać uwierzytelniony, aby
użytkownicy mogli pobrać swoją pocztę e-mail
Jeśli nazwa użytkownika i hasło podane przez klienta
poczty e-mail są takie same jak te na serwerze, użytkownik
zostaje uwierzytelniony i przechodzi do stanu transakcji
W przeciwnym wypadku użytkownik otrzymuje komunikat
o błędzie i nie wolno mu nawiązać połączenia w celu
pobrania poczty e-mail
Aby zapobiec uszkodzeniom magazynu poczty po
uwierzytelnieniu klienta, usługa POP3 zablokowuje
skrzynkę pocztową użytkownika
Protokół POP3
●
●
●
●
Pobranie nowej poczty e-mail, która została dostarczona do
skrzynki pocztowej użytkownika po uwierzytelnieniu
użytkownika (i zablokowaniu skrzynki pocztowej) nie jest
możliwe, dopóki połączenie nie zostanie zakończone
Ponadto w danej chwili ze skrzynką pocztową może być
połączony tylko jeden klient; dodatkowe żądania połączenia
ze skrzynką pocztową są odrzucane
W stanie transakcji klient wysyła polecenia POP3, które
serwer odbiera i odpowiada na nie zgodnie z protokołem
POP3
Wszystkie odebrane przez serwer żądania klienta niezgodne
z protokołem POP3 są ignorowane, a użytkownikowi jest
odsyłany komunikat o błędzie
Protokół POP3
●
●
●
●
Stan aktualizacji zamyka połączenie między klientem a
serwerem
Jest to ostatnie polecenie wysłane przez klienta
Po zamknięciu połączenia magazyn poczty zostaje
zaktualizowany, aby uwzględnić zmiany wprowadzone w
czasie, gdy użytkownik był połączony z serwerem poczty
Gdy na przykład pobieranie wiadomości e-mail przez
użytkownika zostanie zakończone pomyślnie, pobrana
wiadomość e-mail zostaje zaznaczona do usunięcia, a
następnie usunięta z magazynu poczty, o ile klient poczty
e-mail użytkownika nie jest skonfigurowany, aby stało się
inaczej
Przykład sesji POP3
●
●
Na kolejnych slajdach przedstawiona jest przykładowa sesja
POP3 z serwerem www.poczta.o2.pl, w której klient
kolejno:
–
podaje identyfikator użytkownika (user)
–
podaje hasło (pass)
–
prosi o listę oczekujących wiadomości (stat)
–
kopiuję kolejne wiadomości z serwera (retr)
–
kasuje wiadomość po jej ściągnięciu (dele)
–
kończy sesję (quit)
Dokładny opis mechanizmu protokołu POP3 można znaleźć
w dokumencie RFC 1939 (http://tools.ietf.org/html/rfc1939)
Przykład sesji POP3
C: Probuje znalezc adres IP maszyny: poczta.o2.pl
C: Adres poczta.o2.pl to 193.17.41.99
C: Lacze sie z 193.17.41.99:110
C: Polaczony.
S: odppowiedz serwera: +OK POP3 poczta.o2.pl Ready
C: Logowanie uzytkownika: [email protected]
S: odppowiedz serwera: +OK
C: Uwierzytelnianie uzytkownia: [email protected]
S: odppowiedz serwera: +OK
C: Zalogowany: [email protected]
C: Sprawdzanie czy sa nowe wiadomosci …
S: odppowiedz serwera: +OK 1 1513
C: Nowych wiadomosc: 1. Całkowity rozmiar: 1513
C: pobieranie kolejnych wiadomości:
S: odppowiedz serwera: +OK
<….............widaomości......................>
C: Kończenie sesji...
S: odppowiedz serwera: +OK
Komunikacja połączeniowa
(SOCK_STREAM)
●
Gniazda: główna koncepcja komunikacji połączeniowej
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
Pliki nagłówkowe oraz potrzebne makra
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <cstdlib>
#define USERNAME
#define PASSWORD
#define POPSERV
#define SERVICE
#define KEEPM
"[email protected]"
"pipus1234"
"poczta.o2.pl"
"pop-3"
/* nazwa użytkownika POP3 */
/* haslo do skrzynki pocztowej */
/* serwer POP3 */
/* korzystamy z POP3 */
/* kasowanie wiadomości */
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
Poniższy fragment kodu tworzy gniazdo do komunikacji
strumieniowej w domenie Internetowej: zostaną wykorzystane
protokoły: IP oraz TCP
int main() {
int sd,
/* deskryptor gniazda
*/
ret,
/* kod powrotu funkcji
*/
nrmsg = 0; /* ilość nowych wiadomości w skrzynce POP3 */
long int msgsize;
/* łączna wielkość nowych wiadomości
*/
struct sockaddr_in saddr; /* adres gniazda serwera POP3
*/
struct servent *srvent; /* struktura zawierająca numer portu POP3 */
struct hostent *sent; /* z tej struktury odczytamy adres IP serwera */
char msgbuf[1024],
/* bufor na wiadomosci */
buf[256];
/* bufor na komendy/odpowiedzi protokołu POP3 */
sd = socket (PF_INET, SOCK_STREAM, 0);
if (sd < 0) {
std::cerr<<"socket()"<<std::endl;
return 1;
}
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
Poniżej została użyta pomocnicza funkcja getservbyname(). Zwraca
ona wskaźnik do struktury servent, z której później zostanie
odczytany numer portu dla usługi SERVICE ("pop-3") korzystającej z
protokołu "tcp"
srvent = getservbyname (SERVICE, "tcp");
if (!srvent) {
std::cerr<<"getservbyname()"<<std::endl;
return 1;
}
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
●
●
Funkcja gethostbyname() zwraca wskaźnik do struktury hostent
Na podstawie jedynego argumentu (POPSERV) zostanie sturktura
hostnet uzupełniona o potrzebne dane, które umożliwią zamianę
domeny serwera POP3 na jego 32-bitowy adres IP
Funkcja inet_ntoa() zamieni 32-bitowy adres IP na jego odpowiednik
w notacji xxx.xxx.xxx.xxx
std::cout<<"Probuje znalezc adres IP maszyny: "<<POPSERV<<std::endl;
sent = gethostbyname (POPSERV);
if (!sent) {
std::cerr<<"gethostbyname()"<<std::endl;
return 1;
} else {
in_addr in;
memcpy(&in.s_addr, *sent->h_addr_list, sizeof(in.s_addr));
std::cout<<"Adres "<<POPSERV<<" to "<<inet_ntoa(in)<<std::endl;
}
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
W dalszej części programu zostanie wypełniona funkcja struktura
sockaddr_in, która jest niezbędna do nawiązania połączenia connect()
saddr.sin_family = sent->h_addrtype;
saddr.sin_port = srvent->s_port;
memcpy((char *) &saddr.sin_addr, sent->h_addr, sent->h_length);
in_addr in;
memcpy(&in.s_addr, *sent->h_addr_list, sizeof(in.s_addr));
std::cout<<"Lacze sie z "<<inet_ntoa(in)<<":"<<ntohs (srvent->s_port)<<std::endl;
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
●
●
Następnie przy pomocy funkcji connect() następuję połączenie ze zdalnym
procesem/serwerem
Należy zwrócić uwagę na typ argumentów funkcji, która oczekuje m.in.
wskaźnika do struktury sockaddr, a zmienna saddr jest wskaźnikiem do
struktury sockaddr_in – dlatego też należy odpowiednio zmienić typ (ang.
cast) zmiennej saddr
Warto sprawdzić, czy funkcja connect() nie zwróciła błędu, gdzie
najczęstszymi przyczynami błędu są: nieobecność żadnego procesu
nasłuchującego na wybranym porcie zdalnego hosta, brak uprawnień do
połączenia się ze zdalnym serwisem oraz wyczerpanie limitu czasowego na
połączenie
ret = connect (sd, (struct sockaddr *) &saddr, sizeof (saddr));
if (ret < 0) {
std::cerr<<"connect()"<<std::endl;
return 1;
} else {
std::cout<<"Polaczony.\n";
}
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
Kolejne linijki kodu:
memset(buf, 0, sizeof (buf));
recv (sd, buf, sizeof (buf), 0);
if( chceck (buf) ) return 1;
std::cout<<"Logowanie uzytkownika: "<<USERNAME<<std::endl;
sprintf (buf, "USER %s\r\n", USERNAME);
send (sd, buf, strlen (buf), 0);
memset(buf, 0, sizeof (buf));
recv (sd, buf, sizeof (buf), 0);
if( chceck (buf) ) return 1;
std::cout<<"Uwierzytelnianie użytkownia: "<<USERNAME<<std::endl;
sprintf (buf, "PASS %s\r\n", PASSWORD);
send (sd, buf, strlen (buf), 0);
memset(buf, 0, sizeof (buf));
recv (sd, buf, sizeof (buf), 0);
if( chceck (buf) ) return 1;
std::cout<<"Zalogowany: "<<USERNAME<<std::endl;
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
●
●
W celu wymiany informacji ze zdalnym serwerem POP3 zostały użyte
funkcje send() oraz recv()
Komunikacja odbywa się zgodnie z protokołem POP3 (RFC 1081)
Funkcja check sprawdza, czy serwer zwrócił odpowiedź '+OK' na wysłane
przez klienta POP3 polecenie:
int chceck (char *b) {
if (!strncmp (b, "+OK", 3)) {
std::cout<<"odppowiedz serwera: "<<b<<std::endl;
return 0;
} else {
std::cerr<<"Blad! Serwer zwrocil odpowiedz: "<<b<<" Koniec...\n";
return 1;
}
}
●
●
Jeżeli w którymkolwiek momencie utworzonej sesji, serwer wysłał
odpowiedź zawierającą na początku wiadomości inny ciąg znaków niż '+OK'
to oznacza błąd sesji/komunikacji serwer-klient
W takim przypadku należy zakończyć działanie programu
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
●
Jeżeli poprzedni fragment kodu zakończył się powodzeniem, oznacza to że
użytkownik został zalogowany na serwerze, jak również będzie mógł
przystąpić do pobierania poczty
W pierwszej kolejności należy sprawdzić czy na serwerze są nowe
wiadomości:
std::cout<<"Sprawdzanie czy sa nowe wiadomosci ..."<<std::endl;
sprintf (buf, "STAT\r\n");
send (sd, buf, strlen (buf), 0);
memset(buf, 0, sizeof (buf));
recv (sd, buf, sizeof (buf), 0);
if( chceck (buf) ) return 1;
nrmsg = std::atoi(&buf[4]);
msgsize = std::atoi(&buf[6]);
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
●
W pierwszej kolejności zostaje sprawdzone czy dostępne są nowe
wiadomości
Następnie znajduje się główna pętla odpowiedzialna za pobranie kolejnych
wiadomości:
if (nrmsg > 0) {
std::cout<<"Nowych wiadomosc: "<<nrmsg<<". Całkowity rozmiar: "<<msgsize<<std::endl;
for (int n = 1; n <= nrmsg; n++) {
sprintf (buf, "RETR %i\r\n", n);
send (sd, buf, strlen (buf), 0);
memset(buf, 0, sizeof (buf));
recv (sd, buf, sizeof (buf), MSG_PEEK);
if( chceck (buf) ) return 1;
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
●
●
●
W obrębie pętli while pobierane są kolejne części transmitowanej
wiadomości – wynik tego działania jest zapisany do bufora msgbuf
Dwa warunki if służą do odpowiedniego ustawienia zmiennych str, end oraz
msg_end
Umożliwia to wycięcie z wiadomości komuników, które nie są jej częścią
('+OK' i sekwencja CRLF.CRLF)
Wartość zmiennej msg_end zostaje ustawiona na 1 jeśli buforze msgbuf
zawarta jest ostatnia część transmitowanej wiadomości (czyli na końcu
msgbuf jest tylko CRLF.CRLF)
memset(msgbuf, 0, sizeof (msgbuf));
while (recv (sd, msgbuf, sizeof (msgbuf), 0) > 0) {
char *str = msgbuf, *end = msgbuf + strlen (msgbuf);
int msg_end = 0;
if (!strncmp (msgbuf, "+OK", 3))
str = strchr (msgbuf, '\12') + 1;
if (!strncmp (msgbuf + strlen (msgbuf) - 5, "\15\12.\15\12", 5)) {
end = strrchr (msgbuf, '\12') - 4;
msg_end = 1;
}
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
●
W dalszej części programu wyświetlany jest kawałek odfiltrowanej
wiadomości
Jeżeli wiadomość została już cała pobrana wychodzimy z pętli
(msg_end==1).
std::cout<<str;
memset(msgbuf, 0, sizeof (msgbuf));
if (msg_end) {
std::cout<<std::endl;
break;
}
}
std::cout<<"OK\n";
Uproszczony klient usługi POP3
bazujący na komunikacji połączeniowej
●
●
Ostatni fragment kodu kasuje pobraną wiadomość (jeśli nie jest
zdefiniowane makro KEEPM)
Następnie przesyłana jest do serwera POP3 komenda 'QUIT', która kończy
sesję oraz kończy działanie programu
#ifndef KEEPM
std::cout<<"Usuwam wiadomosc nr."<<n<<"... "<<std::endl;
sprintf (buf, "DELE %i\r\n", n);
send (sd, buf, strlen (buf), 0);
recv (sd, buf, sizeof (buf), 0);
chceck (buf);
printf ("Wiadomosc usunieta\n");
#endif
}
}
std::cout<<"Zakończenie sesji... ";
sprintf (buf, "QUIT\r\n");
send (sd, buf, strlen (buf), 0);
recv (sd, buf, sizeof (buf), 0);
if( chceck (buf) ) return 1;
return 0;
}