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; }