Web51 SMTP

Web 51 - Souborovy system  software.html  51 - Vývojový systém - vzorové projekty
Přenos e-mailů protokolem SMTP je textový. Celá předávání zprávy je ze strany odesílatele e-mailu (v tomto případě WEB51) řízen několika klíčovými slovy. Odpovědi serveru jsou ve formě číselného chybového kódu následovaného doplňujícím textem. Odesílání e-mailů systémem WEB51 je řízeno dvěma jednoduchými stavovými automaty. První na základě obdržených chybových kódů řídí odesílání jednotlivých částí zprávy. Druhý je určen pro zpracování přijatého řádku a převod textové formy chybových kódů do binární formy, zpracovávané prvním automatem.

Příklad 4.1.

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: AhojTest.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ů ukončujících řádek.


Realizace SMTP

WEB51 realizuje protokol SMTP pomocí čtyř procedur :

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í.

Příklad 4.2.

sendSMTP:
	mov	smtpNo, r7
	mov	smtpState, #0
	sjmp	processSMTP

Příklad 4.3.

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:

Příklad 4.6.

; 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

Příklad 4.7.

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	

Příklad 4.8.

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

Příklad 4.9.

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

Příklad 4.10.

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

Příklad 4.11.

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

Příklad 4.12.

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

Příklad 4.13.

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

Příklad 4.14.

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

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.

Příklad 4.15.

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	Lbreak

Více podrobností najdete v příkladech..




Sponzored by LPhard Ltd. Graphics by GIMP Created by EasyPad

(c)Copyright 2000, 2001, HW server & Radek Benedikt
Web51@HW.cz, Web51.HW.cz

Final applications of the Web51 : www.HWgroup.cz
Web 51 - Souborovy system  Obsah  51 - Vývojový systém - vzorové projekty