Web51 SMTP |
|
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 there 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
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.
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.
sendSMTP: mov smtpNo, r7 mov smtpState, #0 sjmp processSMTP
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:
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
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
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
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
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
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
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
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
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
Processing of data passed to the procedure processSMTP is managed by the second state machine. This one has only four states.
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.
| Web51 description | News | FAQ | ORDER FORM | DOWNLOAD | Links |
| (c)Copyright 2000 - 2002, HW server & Radek Benedikt
Web51@HW.cz, Web51.HW.cz |
|