Web51 SMTP |
Ukažme si nejprve celou komunikaci na příkladu, zachyceném analyzátorem paketů. V našem případě má WEB51 IP adresu 192.168.0.77, adresa 192.168.0.253 patří SMTP serveru.
192.168.0.77.25 > 192.168.0.253.25: S 1080189804:1080189804(0) win 1460(ttl 64, id 1, len 44) 192.168.0.253.25 > 192.168.0.77.25: S 1807090483:1807090483(0) ack 1080189805 win 16060 (DF) 192.168.0.77.25 > 192.168.0.253.25: . 1:1(0) ack 1 192.168.0.253.1024 > 192.168.0.77.113: S 1806984606:1806984606(0) win 16060 192.168.0.77.113 > 192.168.0.253.1024: R 0:0(0) 192.168.0.253.25 > 192.168.0.77.25: P 1:73(72) ack 1 220 debian localnet ESMTP Exim.2.05.#1 Tue, 19 Mar 2002 07:04:42.+0100 192.168.0.77.25 > 192.168.0.253.25: P 1:13(12) ack 73 HELO web51 192.168.0.253.25 > 192.168.0.77.25: . 73:73(0) ack 13 192.168.0.253.25 > 192.168.0.77.25: P 73:121(48) ack 13 250 debian.localnet Hello web51 [192.168.0.77] 192.168.0.77.25 > 192.168.0.253.25: P 13:38(25) ack 121 MAIL FROM: 192.168.0.253.25 > 192.168.0.77.25: P 121:165(44) ack 38 250 is syntactically correct 192.168.0.77.25 > 192.168.0.253.25: P 38:71(33) ack 165 RCPT TO: 192.168.0.253.25 > 192.168.0.77.25: P 165:219(54) ack 71 250. is syntactically correct 192.168.0.77.25 > 192.168.0.253.25: P 71:77(6) ack 219 DATA 192.168.0.253.25 > 192.168.0.77.25: P 219:275(56) ack 77 354 Enter message, ending with "." on a line by itself 192.168.0.77.25 > 192.168.0.253.25: P 77:109(32) ack 275 Subject: Ahoj Test.Message . 192.168.0.253.25 > 192.168.0.77.25: . 275:275(0) ack 109 192.168.0.253.25 > 192.168.0.77.25: P 275:303(28) ack 109 250 OK id=16nCji-00003p-00 192.168.0.77.25 > 192.168.0.253.25: P 109:115(6) ack 303 QUIT 192.168.0.253.25 > 192.168.0.77.25: . 303:303(0) ack 115 192.168.0.253.25 > 192.168.0.77.25: P 303:343(40) ack 115 221 debian.localnet closing connection
Pohledem do výpisu však na čtvrtém a pátem řádku nacházíme malý problém, SMTP server se pokouší protokolem IDENT o ověření s kým má tu čest. Na tento pokus o ověření je vhodné korektně odpovědět, například tak jak je naznačeno ve výpisu, odmítnutím spojení. Standartní TCP stack WEB51 na pokus o prohledávání portů mlčí, nicméně je možno jej nakonfigurovat parametrem NOIDENT, při překladu knihovny, aby na pokus o spojení na portu 113 odpověděl TCP paketem obsahujícím nastavený RST bit.
Zprávy k odesílání, včetně subjektu, adresy odesílatele a příjemce jsou uloženy ve standartním souborovém systému WEB51. Obsluha souborového systému WEB51 je u SMTP protokolu stejná jako u obsluhy HTTP protokolu. Soubory mohou obsahovat volání CGI skriptů a podmíněný text. Jejich struktura je popsána v části věnující se HTTP protokolu a jeho souborovému systému.
Standartně jsou podporovány tato jména souborů:Znak "$" v názvu souboru je zde použit pouze jako demonstrační. Ve skutečném jménu souboru je místo něj příslušný jednobytový identifikátor zprávy, tak jak je předáván proceduře sendSMTP. Ve vzorovém příkladu je tímto identifikátorem znak "0". Žádný z těchto souborů nepředpokládá, že by obsahoval na konci dvojici znaků
Vysílání e-mailové zprávy protokolem SMTP je inicializováno voláním procedury sendSMTP. Ta uloží identifikátor zprávy a inicializuje stavový automat řídící zpracování SMTP stavů a předá mu řízení.
sendSMTP: mov smtpNo, r7 mov smtpState, #0 sjmp processSMTP
Probeme si nyní SMTP stavový automat, řídící celou komunikaci s e-mail serverem
processSMTP: mov r7,a ; switch (smtpState) { mov a,smtpState add a,acc ;sjmp = 2 byte add a,smtpState ;ljmp = 3 byte add a,#LOW(stateSMTPtable) push Acc ;push low addr mov a,#0 addc a,#HIGH(stateSMTPtable) push Acc ;push high addr ret ;and Go! stateSMTPtable: ljmp Lcase0 ljmp Lcase1 ljmp Lcase2 ljmp Lcase3 ljmp Lcase4 ljmp Lcase5 ljmp Lcase6 ljmp Lcase7
SMTP stavový automat začíná stavem 0, který není výjmečně volán po přijetí TCP paketu, ale z procedury sendSMTP. V tomto stavu je pomocí volání ConnectTCP1 zahájeno vyjednávání o navázání spojení. Přechod ze stavu 0 do stavu 1 je, díky vyjednávání o navázání spojení, o něco složitější. Zobrazíme-li si sekvenci volání uvidíme:
openSMTP: setb flag_out ret
.section fast, #alloc jnb flag_out, noout clr flag_out lcall OutTCP1 ;Syn Ack from MAIL server, REPLY Ack noout:
; case 0: Lcase0: ;; open connection to SMTP server (port 25) mov r7,#LASTSTACK1 lcall changeStack mov a,tcpState jnz est25ch ;if Connect Established, don't make new lcall ConnectTCP1 est25ch: flushAndNextCase: ;; flush end of received data mov data_len+1,#1 ;LSB mov data_len,#0 ;MSB ;; reset data state machine mov smtpCState,#0 ;; next SMTP state inc smtpState ret
Stav 1 SMTP stavového automatu očekává úvodní hlášku mail serveru. Po jejím přijetí na ni odpoví příkazem HELO
; case 1: Lcase1: ;; wait for REPLY "220 debian.localnet ESMTP..." lcall processSMTPcharacter cjne A,#3,Lc1 mov a,replyNum cjne A,#LOW(220),Lx1 ;; SEND "helo web51" lcall send_string .asciz "HELO web51\r\n" sjmp flushAndNextCase Lx1: Lc1: ret
Stav 2 po potvrzení přijetí příkazu HELO zahájí vysílání hlavičky zprávy. Nejprve je z příslušného souboru from$.txt předána mail serveru adresa odesílatele.
; case 2: Lcase2: ;; wait for REPLY "250 debian.localnet Hello web51..." lcall processSMTPcharacter cjne A,#3,Lc2 mov a,replyNum cjne A,#LOW(250),Lx2 ;; SEND "mail from:" lcall send_string .asciz "MAIL FROM:" mov a, #LOW('f'+'r'+'o'+'m'+'.'+'t'+'x'+'t') add a,smtpNo mov fileID+1,a mov a, #HIGH('f'+'r'+'o'+'m'+'.'+'t'+'x'+'t') addc a,#0 mov fileID,a lcall searchfile ;r4 file type ;dptr file start ;r2r3 file end jnc fnd2 ; lcall send_string .asciz "ErrFrom\r\n" sjmp flushAndNextCase fnd2: ;r2 ;MSB file end ;r3 ;LSB file end ;dph ;MSB file start ;dpl ;LSB file start ;r5 ;filesystem ID mov a,r5 lcall sendFile lcall send_string .asciz "\r\n" sjmp flushAndNextCase Lx2: Lc2: ret
Stav 3 po potvrzení přijetí příkazu MAIL předá z příslušného souboru to$.txt mail serveru adresu příjemce.
; case 3: Lcase3: ;; wait for REPLY "250..." lcall processSMTPcharacter cjne A,#3,Lc3 mov a,replyNum cjne A,#LOW(250),Lx3 ;; SEND "rcpt to: " lcall send_string .asciz "RCPT TO:" mov a, #LOW('t'+'o'+'.'+'t'+'x'+'t') add a,smtpNo mov fileID+1,a mov a, #HIGH('t'+'o'+'.'+'t'+'x'+'t') addc a,#0 mov fileID, a lcall searchfile ;r4 file type ;dptr file start ;r2r3 file end jnc found3 ; lcall send_string .asciz "ErrTo\r\n" ljmp flushAndNextCase found3: ;r2 ;MSB file end ;r3 ;LSB file end ;dph ;MSB file start ;dpl ;LSB file start ;r5 ;filesystem ID mov a,r5 lcall sendFile lcall send_string .asciz "\r\n" ljmp flushAndNextCase Lx3: Lc3: ret
Stav 4 po potvrzení přijetí příkazu RCPT zahájí přenos dat vysláním úvodního příkazu DATA
; case 4: Lcase4: ;; wait for REPLY "250..." lcall processSMTPcharacter cjne A,#3,Lc4 mov a,replyNum cjne A,#LOW(250),Lx4 ;; SEND "data" lcall send_string .asciz "DATA\r\n" ljmp flushAndNextCase Lx4: Lc4: ret
Stav 5 po potvrzení přijetí příkazu DATA předá z příslušného souboru subject$.txt mail serveru subjekt zprávy. Za ním odešle vlastní text e-mailu z příslušného souboru text$.txt. Celá e-mailová zpráva je následně ukončena sekvencí
; case 5: Lcase5: ;; wait for REPLY "354 Enter message, ..." lcall processSMTPcharacter cjne A,#3,Lc5 mov a,replyNum cjne A,#LOW(354),Lx5 ;; SEND "Subject: Ahoj" ;; SEND "MESSAGE " ;; SEND ". " lcall send_string .asciz "Subject: " mov a, #LOW('s'+'u'+'b'+'j'+'e'+'c'+'t'+'.'+'t'+'x'+'t') add a,smtpNo mov fileID+1,a mov a, #HIGH('s'+'u'+'b'+'j'+'e'+'c'+'t'+'.'+'t'+'x'+'t') addc a,#0 mov fileID, a lcall searchfile ;r4 file type ;dptr file start ;r2r3 file end jnc found5a ; lcall send_string .asciz "ErrSubject\r\n.\r\n" ljmp flushAndNextCase found5a: ;r2 ;MSB file end ;r3 ;LSB file end ;dph ;MSB file start ;dpl ;LSB file start ;r5 ;filesystem ID mov a,r5 lcall sendFile lcall send_string .asciz "\r\n" mov a, #LOW('t'+'e'+'x'+'t'+'.'+'t'+'x'+'t') add a,smtpNo mov fileID+1,a mov a, #HIGH('t'+'e'+'x'+'t'+'.'+'t'+'x'+'t') addc a,#0 mov fileID, a lcall searchfile ;r4 file type ;dptr file start ;r2r3 file end jnc found5b ; lcall send_string .asciz "ErrText\r\n.\r\n" ljmp flushAndNextCase found5b: ;r2 ;MSB file end ;r3 ;LSB file end ;dph ;MSB file start ;dpl ;LSB file start ;r5 ;filesystem ID mov a,r5 lcall sendFile lcall send_string .asciz "\r\n.\r\n" ljmp flushAndNextCase Lx5: Lc5: ret
Stav 6 po potvrzení přijetí e-mailové zprávy ukončí celou komunikaci odesláním příkazu QUIT
; case 6: Lcase6: ;; wait for REPLY "250 OK id=..." lcall processSMTPcharacter cjne A,#3,Lc6 mov a,replyNum cjne A,#LOW(250),Lx6 lcall send_string ;; SEND "quit" .asciz "QUIT\r\n" ljmp flushAndNextCase Lx6: Lc6: ret
Stav 7 po potvrzení přijetí příkazu QUIT uzavře TCP/IP spojení
; case 7: Lcase7: ;; wait for REPLY "221 debian.localnet closing connection..." lcall processSMTPcharacter cjne A,#3,Lc7 mov a,replyNum cjne A,#LOW(221),Lx7 ;; close connection to SMTP server lcall CloseTCP1 mov smtpState, #0 Lx7: Lc7: ret
Zpracování přijatého bloku dat z mail serveru je obdobné zpracování popsanému v kapitole WEB51 TCP sockets. Data z přijatého paketu jsou po bytech posílána proceduře processSMTP.
smtp: ;;process max. data_len bytes mov a,data_len+1 ;LSB mov workreg+3,a ;LSB mov workreg+2,data_len ;MSB orl a,data_len ;MSB jz noSMTP sgetbuf:mov Timeout1,#ethtiming ;restart retry timer ;; Retry1 = ETHRETRY1; mov Retry1,#ETHRETRY1 setb flag1retry ;; NEW data in packet, send delayed Ack LCALL pcode ;; copy_r2s (buf, data_addr, sizeof(buf)); .pcode pr2s BYTE buf, @data_addr, #BYTE sizeofbuf ;copy smtp data ;; data_addr += sizeof(buf); .pcode paddwi BYTE data_addr, #BYTE sizeofbuf .byte 0 mov R0,#buf-1 tnxtin:inc R0 mov a,#(buf + sizeofbuf) xrl a,R0 jz sgetbuf mov a,data_len jnz tdata mov a,data_len+1 jnz tdata sjmp tallsend tdata: mov a,@r0 lcall processSMTP ;; data_len--; mov a,data_len+1 ; LSB dec a mov data_len+1,a cjne A,#0xFF,tnxtin dec data_len ; MSB sjmp tnxtin tallsend: ; Acknowledge only processed data ;; tx_eth_pkt.pkt.ip.ipdata.tcp.tcpheader.Ack = ;; rcvSeq + sendbytes; mov R7,workreg+3 ;LSB mov R6,workreg+2 ;MSB ljmp AckBytes noSMTP: ret
Zpracování dat, předávaných proceduře processSMTP je zabezpečováno druhým stavovým automatem. Ten má pouhé čtyři stavy.
processSMTPcharacter: ; switch (smtpCState) { ..... ; case 0: LCcase0:;; skip non numeric on begin of line mov a,r7 add a,#-'0' cjne a,#9+1,.+3 jnc Lbreak ; out of range '0'..'9' mov replyNum,a inc smtpCState ; smtpCState = 1 ; case 3: LCcase3:;; end of run Lbreak: mov a,smtpCState ret ; return smtpCState ; case 1: LCcase1:mov a,r7 add a,#-'0' cjne a,#9+1,.+3 jnc Lend1 xch a,replyNum mov B,#10 mul ab add a,replyNum mov replyNum,a ; mov replyNum+1,b sjmp Lbreak Lend1: inc smtpCState ; smtpCState = 2 ; case 2: LCcase2:;; wait for control char mov a,r7 anl a,#0xF0 jz Lend2 sjmp Lbreak Lend2: inc smtpCState ; smtpCState = 3 sjmp LbreakVíce podrobností najdete v příkladech..
POPIS Web51 | NOVINKY | FAQ | OBJEDNÁVKA | DOWNLOAD |
(c)Copyright 2000, 2001, HW server & Radek Benedikt
Web51@HW.cz, Web51.HW.cz Final applications of the Web51 : www.HWgroup.cz |