发送邮件使用的是SMTP协议,也称简单邮件传输协议。现在在网上搜到的代码或者例子大都是SMTP协议的,但是而今大多数邮件服务器都已经采用验证式的STMP协议了,所以这些代码或者例子常常得到无法调试成功的结果,这给了我们初学者一个不小的打击。其实所谓验证式的SMTP协议,只不过是在原来的SMTP协议的基础上加上了一个用户密码验证,验证后的协议内容跟原来别无二致。这种新的验证式SMTP协议称为ESMTP,也即扩展简单邮件传输协议。
之所以要扩展SMTP协议,是为了防止垃圾邮件的日益泛滥,阻止未经验证的垃圾邮件制造者随意使用这项服务。但我个人认为ESMTP并没达到目的(粗浅认识),现在的免费邮箱一大把,随便注册一个一样可以发一大堆垃圾邮件啊。(不过由于是经过了验证的,经过服务器的垃圾邮件过滤就可以把你这个邮箱帐号给~~,"反正是黑名单吧")。
ESMTP协议是一种C/S应答式协议,邮件客户端发送一个请求,服务器返回相应的内容。
C:连接服务器... (通过connect函数)
S:220
C:HELO username
S:250
C:AUTH LOGIN
S:334
C:Base64(username) (经过Base64编码后的用户名)
S:334
C:Base64(password) (经过Base64编码后的密码)
S:235
C:MAIL FROM: <XiaoMing@163.com> ()
S:250
C:RCPT TO: <XiaoHong@163.com> ()
S:250
C:DATA
S:354
C:
S:250
C:
S:221
.386
.model flat, stdcall
option casemap:none
include /masm32/include/windows.inc
include /masm32/include/kernel32.inc
include /masm32/include/user32.inc
include /masm32/include/comdlg32.inc
include /masm32/include/wsock32.inc
includelib /masm32/lib/kernel32.lib
includelib /masm32/lib/user32.lib
includelib /masm32/lib/comdlg32.lib
includelib /masm32/lib/wsock32.lib
.const
IDI_MAIN equ 1
IDC_EDIT_ATTACHMENT equ 3008
IDC_BUTTON_BROWSE equ 3009
DlgMain equ 1
IDC_EDIT_SMTPSERVER equ 1
IDC_EDIT_USERNAME equ 2
IDC_EDIT_PASSWORD equ 3
IDC_EDIT_FROM equ 4
IDC_EDIT_TO equ 5
IDC_EDIT_SUBJECT equ 6
IDC_EDIT_CONTENT equ 7
IDC_BUTTON_SEND equ 8
IDC_VAl equ 9
TCP_PORT equ 25
MAIL_HELO equ 1
MAIL_LOGIN equ 2
MAIL_USER equ 3
MAIL_PASS equ 4
MAIL_FROM equ 5
MAIL_RCPT equ 6
MAIL_DATA equ 7
MAIL_DOT equ 8
MAIL_QUIT equ 9
.data
;下面是为了方便调试,预设的各项参数:
sz1 db "smtp.163.com", 0
;sz2 db "zhangyuming123456", 0
;sz3 db "789789", 0
;sz4 db "zhangyuming123456@163.com", 0
;sz5 db "zhangyuming123456@163.com", 0
;sz6 db "最近如何?", 0
;sz7 db "你好吗?", 0
szCaption db "ESMTP邮件发送程序",0
szServerIPErr db "SMTP服务器地址解析失败!",0
szConnectErr db "连接服务器出错!",0
szSendOK db "邮件发送成功!",0
szSendErr db "邮件发送失败!",0
szHeloFmt db "HELO %s", 13, 10, 0
szLoginFmt db "AUTH LOGIN", 13, 10, 0
szUserPassFmt db "%s", 13, 10, 0
szFromFmt db "MAIL FROM: <%s>", 13, 10,0
szRcptFmt db "RCPT TO: <%s>", 13, 10, 0
szDataFmt db "DATA", 13, 10,0
szQuitFmt db "QUIT",13,10,0
szTextFmt db "From: <%s>", 13, 10
db "To: <%s>", 13, 10
db "Subject: %s", 13, 10
db "MIME_Version: 1.0", 13, 10
db "Content-type:text/plain;Charset=GB2312", 13, 10
db "Content-Transfer-Encoding:8bit", 13, 10, 13, 10
db "%s", 13, 10,13,10, ".",13, 10, 0
;Base64 -> ASCII mapping table
base64_alphabet db "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",0
szFmt db "%d",0
.data?
hInstance dd ?
hSocket dd ?
hWinMain dd ?
szServer db 256 dup (?)
szUser db 256 dup (?)
szPass db 256 dup (?)
szFrom db 256 dup (?)
szRcpt db 256 dup (?)
szSubject db 256 dup (?)
szText db 30000 dup (?)
szBuf1 db 65536 dup (?)
.code
Base64Encode proc uses ebx edi esi source:DWORD, destination:DWORD
LOCAL sourcelen:DWORD
invoke lstrlen, source
mov sourcelen, eax
mov esi, source
mov edi, destination
@@base64loop:
xor eax, eax
.if sourcelen == 1
lodsb ;source ptr + 1
mov ecx, 2 ;bytes to output = 2
mov edx, 03D3Dh ;padding = 2 byte
dec sourcelen ;length - 1
.elseif sourcelen == 2
lodsw ;source ptr + 2
mov ecx, 3 ;bytes to output = 3
mov edx, 03Dh ;padding = 1 byte
sub sourcelen, 2 ;length - 2
.else
lodsd
mov ecx, 4 ;bytes to output = 4
xor edx, edx ;padding = 0 byte
dec esi ;source ptr + 3 (+4-1)
sub sourcelen, 3 ;length - 3
.endif
xchg al,ah ;flip eax completely
rol eax, 16 ;can this be done faster
xchg al,ah
@@:
push eax
and eax, 0FC000000h ;get the last 6 high bits
rol eax, 6 ;rotate them into al
mov al, BYTE ptr [offset base64_alphabet + eax] ;get encode character
stosb ;write to destination
pop eax
shl eax, 6 ;shift left 6 bits
dec ecx
jnz @B ;loop
cmp sourcelen, 0
jnz @@base64loop ;main loop
mov eax, edx ;add padding and null terminate
stosd
ret
Base64Encode endp
_GetText proc uses ebx esi
invoke GetDlgItemText,hWinMain,IDC_EDIT_SMTPSERVER,addr szServer,256
invoke GetDlgItemText,hWinMain,IDC_EDIT_USERNAME,addr szUser,256
invoke GetDlgItemText,hWinMain,IDC_EDIT_PASSWORD,addr szPass,256
invoke GetDlgItemText,hWinMain,IDC_EDIT_FROM,addr szFrom,256
invoke GetDlgItemText,hWinMain,IDC_EDIT_TO,addr szRcpt,256
invoke GetDlgItemText,hWinMain,IDC_EDIT_SUBJECT,addr szSubject,256
invoke GetDlgItemText,hWinMain,IDC_EDIT_CONTENT,addr szText,30000
ret
_GetText endp
_EnableCtrl proc _State
local nID
mov nID,1
.while nID<=8
invoke GetDlgItem,hWinMain,nID
invoke EnableWindow,eax,_State
inc nID
.endw
ret
_EnableCtrl endp
_WaitFor proc _nNum
local @szBuf[1024]:BYTE
local @stFdSet:fd_set,@stTimeval:timeval
;local @nNum
mov @stFdSet.fd_count,1
push hSocket
pop @stFdSet.fd_array
mov @stTimeval.tv_sec,60
mov @stTimeval.tv_usec,0
invoke select,0,addr @stFdSet,NULL,NULL,addr @stTimeval
.if eax==0 || eax==SOCKET_ERROR
invoke MessageBox,hWinMain,addr szSendErr,addr szCaption,MB_OK
invoke _EnableCtrl,TRUE
invoke ExitThread,NULL
;邮件发送失败
.endif
invoke recv,hSocket,addr @szBuf,sizeof @szBuf,0
invoke MessageBox,hWinMain,addr @szBuf,addr szCaption,MB_OK
mov BYTE ptr @szBuf[3],0
invoke SetDlgItemText,hWinMain,IDC_VAl,addr @szBuf
invoke GetDlgItemInt,hWinMain,IDC_VAl,NULL,FALSE
;mov @nNum,eax
;invoke wsprintf,addr @szBuf,addr szFmt,@nNum
;invoke MessageBox,NULL,addr @szBuf,NULL,MB_OK
;invoke wsprintf,addr @szBuf,addr szFmt,_nNum
;invoke MessageBox,NULL,addr @szBuf,NULL,MB_OK
;mov eax,@nNum
.if eax != _nNum
invoke MessageBox,hWinMain,addr szSendErr,addr szCaption,MB_OK
invoke _EnableCtrl,TRUE
invoke ExitThread,NULL
;邮件发送失败
.endif
ret
_WaitFor endp
_SendCmd proc _StateID
mov eax,_StateID
.if eax==MAIL_HELO
invoke wsprintf,addr szBuf1,addr szHeloFmt,addr szUser
.elseif eax==MAIL_LOGIN
invoke lstrcpy,addr szBuf1,addr szLoginFmt
.elseif eax==MAIL_USER
invoke Base64Encode,addr szUser,addr szBuf1
;invoke MessageBox,NULL,addr @szBuf1,NULL,MB_OK
invoke wsprintf,addr szBuf1,addr szUserPassFmt,addr szBuf1
.elseif eax==MAIL_PASS
invoke Base64Encode,addr szPass,addr szBuf1
;invoke MessageBox,NULL,addr @szBuf1,NULL,MB_OK
invoke wsprintf,addr szBuf1,addr szUserPassFmt,addr szBuf1
.elseif eax==MAIL_FROM
invoke wsprintf,addr szBuf1,addr szFromFmt,addr szFrom
.elseif eax==MAIL_RCPT
invoke wsprintf,addr szBuf1,addr szRcptFmt,addr szRcpt
.elseif eax==MAIL_DATA
invoke lstrcpy,addr szBuf1,addr szDataFmt
.elseif eax==MAIL_DOT
invoke wsprintf,addr szBuf1,addr szTextFmt,addr szFrom,addr szRcpt,addr szSubject,addr szText
.else
invoke lstrcpy,addr szBuf1,addr szQuitFmt
.endif
invoke lstrlen,addr szBuf1
invoke send,hSocket,addr szBuf1,eax,0
invoke MessageBox,hWinMain,addr szBuf1,addr szCaption,MB_OK
.if eax==SOCKET_ERROR
invoke MessageBox,hWinMain,addr szSendErr,addr szCaption,MB_OK
invoke _EnableCtrl,TRUE
invoke ExitThread,NULL
;邮件发送失败
.endif
ret
_SendCmd endp
_SendThread proc lParam
local @stSin:sockaddr_in
invoke _GetText
invoke _EnableCtrl,FALSE
invoke RtlZeroMemory,addr @stSin,sizeof @stSin
;invoke _GetHostIP,eax
invoke gethostbyname , addr szServer
mov eax, [eax + 12]
mov eax, [eax]
mov eax, [eax]
.if eax==0
invoke MessageBox,hWinMain,addr szServerIPErr,addr szCaption,MB_OK
JMP smtp_ERROR
;SMTP服务器地址错误
.endif
mov @stSin.sin_addr,eax
mov @stSin.sin_family,AF_INET
invoke htons,TCP_PORT
mov @stSin.sin_port,ax
invoke socket,AF_INET,SOCK_STREAM,0
mov hSocket,eax
invoke connect,hSocket,addr @stSin,sizeof @stSin
.if eax==SOCKET_ERROR
invoke MessageBox,hWinMain,addr szConnectErr,addr szCaption,MB_OK
JMP smtp_ERROR
;邮件发送失败
.endif
invoke _WaitFor,220
invoke _SendCmd,MAIL_HELO
invoke _WaitFor,250
invoke _SendCmd,MAIL_LOGIN
invoke _WaitFor,334
invoke _SendCmd,MAIL_USER
invoke _WaitFor,334
invoke _SendCmd,MAIL_PASS
invoke _WaitFor,235
invoke _SendCmd,MAIL_FROM
invoke _WaitFor,250
invoke _SendCmd,MAIL_RCPT
invoke _WaitFor,250
invoke _SendCmd,MAIL_DATA
invoke _WaitFor,354
invoke _SendCmd,MAIL_DOT
invoke _WaitFor,250
invoke _SendCmd,MAIL_QUIT
invoke _WaitFor,221
invoke MessageBox,hWinMain,addr szSendOK,addr szCaption,MB_OK
invoke closesocket,hSocket
invoke _EnableCtrl,TRUE
ret
smtp_ERROR:
invoke MessageBox,hWinMain,addr szSendErr,addr szCaption,MB_OK
invoke _EnableCtrl,TRUE
ret
_SendThread endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL @stWsa: WSADATA
.if uMsg == WM_CLOSE
invoke WSACleanup
invoke EndDialog, hWnd, 0
.elseif uMsg == WM_INITDIALOG
push hWnd
pop hWinMain
invoke WSAStartup,101h,addr @stWsa
invoke LoadIcon, hInstance, IDI_MAIN
invoke SendMessage, hWnd, WM_SETICON, ICON_SMALL, eax
;下面是为了方便调试,填入预设的各项参数:
invoke SetDlgItemText, hWnd, IDC_EDIT_SMTPSERVER, addr sz1
;invoke SetDlgItemText, hWnd, IDC_EDIT_USERNAME, addr sz2
;invoke SetDlgItemText, hWnd, IDC_EDIT_PASSWORD, addr sz3
;invoke SetDlgItemText, hWnd, IDC_EDIT_FROM, addr sz4
;invoke SetDlgItemText, hWnd, IDC_EDIT_TO, addr sz5
;invoke SetDlgItemText, hWnd, IDC_EDIT_SUBJECT, addr sz6
;invoke SetDlgItemText, hWnd, IDC_EDIT_CONTENT, addr sz7
invoke GetDlgItem,hWnd,IDC_VAl
invoke ShowWindow,eax,SW_HIDE
.elseif uMsg == WM_COMMAND
mov eax,wParam
.if ax==IDC_BUTTON_SEND
push ecx
invoke CreateThread,NULL,0,Offset _SendThread,0,NULL,esp
pop ecx
invoke CloseHandle,eax
.endif
.else
mov eax, FALSE
ret
.endif
mov eax, TRUE
ret
WndProc endp
Start:
invoke GetModuleHandle, NULL
mov hInstance, eax
invoke DialogBoxParam, hInstance, DlgMain, 0, WndProc, 0
invoke ExitProcess, eax
end Start