Web51 SMTP

Filesystem  software.html  projects
The SMTP protocol transfers e-mails in textual form. The entire process is controlled by the sender of the e-mail (WEB51 in this case) using several keywords. Server responds with numeric error codes followed by textual descriptions. The Web51 system uses two simple state machines to send e-mail. The first one controls the transmission of individual portions of the e-mail according to the error codes received. The otehr one processes received lines and converts textual form of error codes into binary values needed by the first state machine.

Example 4.1

First, let's demonstrate the entire communication, as captured by a packet analyzer. In this case, the IP address 192.168.0.77 belongs to Web51, 192.168.0.253 belongs to the SMTP server.

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: Hi thereTest.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

Looking at the listing, there is a small problem shown on the 4th and 5th line. The SMTP server tries to verify the other party's identity using the IDENT protocol. It is desirable to respond in a correct way to such a request; for example, by refusing the connection as shown above. The standard TCP stack of Web51 does not respond to port scans; however, it can be configured by the NOIDENT parameter when compiling the library to respond to connection attempts on port 113 with a TCP packet that has the RST bit set.

Messages to be sent, including the subject line and the sender's and recipient's addresses, are stored in the usual way in the Web51 filesystem. The filesystem works in the same way with SMTP as it does with HTTP. Files may contain calls to CGI scripts as well as conditional texts. The structure is described in the sections that detail the HTTP protocol and its filesystem.

Normally, the following filenames are supported:

The "$" character in the filename is used here for demonstration purposes only. In a real name, it is replaced by a single-byte identifier of the message, as passed to the sendSMTP procedure. In the example, this identifier is "0". These files should not end with the end-of-line character pair CRLF.


SMTP Implementation

Web51 implements SMTP using four procedures:

Transmission of an e-mail message begins with a call to the sendSMTP procedure, which stores the message identifier, initializes the state machine that controls the processing of SMTP states, and starts it.

Example 4.2

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

Example 4.3

Now, let's take a look at the SMTP state machine that controls the entire communication with the SMTP server.

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

The SMTP state machine begins in state 0, the only state that is not called after a TCP packet reception but rather from the sendSMTP procedure. In this state, connection negotiation is started with a call to ConnectTCP1. Transition from state 0 to state 1 is more complex due to the connection negotiation involved. The sequence of calls follows:

Example 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

Example 4.7.

State 1 of the SMTP state machine waits for the initial message from the mail server. When received, it responds with the HELO command.

; 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	

Example 4.8

State 2 begins transmission of the message header when the HELO command has been acknowledged. First, the sender's address from the respective from$.txt file is sent to the mail server.

; 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

Example 4.9.

State 3 transmits the recipient's address from the respective to$.txt file to the mail server as soon as the MAIL command has been acknowledged.

; 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

Example 4.10

State 4 begins the data transmission with the initial DATA command as soon as the RCPT command has been acknowledged.

; 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

Example 4.11

State 5 transmits the message subject from the respective subject$.txt file when the DATA command has been ackowledged. After the subject, it transmits the actual body of the e-mail from the respective text$.txt file. The entire e-mail message is terminated by the sequence .

; 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

Example 4.12

State 6 terminates the session with the QUIT command as soon as the reception of the entire e-mail is acknowledged.

; 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

Example 4.13

State 7 closes the TCP/IP connection as soon as the QUIT command is acknowledged.

; 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

Example 4.14

Processing of the block of data received from the mail server is similar to the processing described in the section WEB51 TCP sockets. Data from the packet are sent byte by byte to the processSMTP procedure.

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

Data processing

Processing of data passed to the procedure processSMTP is managed by the second state machine. This one has only four states.

Example 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

The Examples contain further details.




Sponzored by LPhard Ltd. Graphics by GIMP Created by EasyPad

(c)Copyright 2000 - 2002, HW server & Radek Benedikt
Web51@HW.cz, Web51.HW.cz
Filesystem  Obsah  projects