отправка писем с аттачем из лвс с kerio
От: Black_Moon  
Дата: 15.08.07 12:17
Оценка:
Написал smtp клиента
отправляю письма с аттачем.
Вот какую фичу заметил — аттачи до 8192 байт отправляются отлично, а если >= то при отправке выдает ошибку 10053 (WSAECONNABORTED). Прада если отправлять обычным клиентом (напр. The Bat!), то все ок. Аттачи отправляются кусками по 1364 байта (чтение файла в блоки по 1023 — потом кодирование base64).

Клиент работает на раб. станции в лвс, на сервере стоит winroute kerio.
Пробовал отправлять минуя kerio (подключая интернет напрямую к компу — все идет).

Через свой фтп-клиенет тоже никаких ограничений на файл нет...

Непонятно в каких настройках ковыряться?

Вот еще заметил — если адресат rambler.ru, то в полученый аттач принимает не верно. 1й блок декодирует верно, а перед остальными сам ставит "\r\n"

Код

#include <stdio.h>
#include <winsock2.h>
#include <windows.h>

#define BOUNDARY            "e1215f2aa8c6"

#define BLOCK_SIZE            1023
#define BLOCK_SIZE64                1364
#define MESSAGE_SIZE                64
// smtp reply codes
#define AUTH_OK                334
#define SPASS_OK            235
#define CONNECT_OK            220
#define DATA_OK                354
#define MAIL_OK                250
#define    SQUIT_OK            221

int send_smtp(const char* name, const char* file)
{
    int err, req, res = 0;
    u_int quot, rem;

    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);    
    if (INVALID_SOCKET == s) return -1;

    try
    {
        u_long mode = 1;
        if (0 != ioctlsocket(s, FIONBIO, &mode))        // non-block    
        {
            err = WSAGetLastError();            
            throw err;
        }
        if (0 != _connect(s, "mail.rambler.ru", 25)) throw -1;        

        char rbuf[BLOCK_SIZE], sbuf[MESSAGE_SIZE];
        
        memset(rbuf, 0, BLOCK_SIZE);
        err = _recv(s, rbuf, BLOCK_SIZE);
        if (err != 0) throw err;
        
        req = _request(rbuf);
        if (CONNECT_OK != req) throw req;

        sprintf_s(sbuf, MESSAGE_SIZE, "EHLO %s\r\n", "mail.rambler.ru");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;

        memset(rbuf, 0, BLOCK_SIZE);
        err = _recv(s, rbuf, BLOCK_SIZE);
        if (err != 0) throw err;

        req = _request(rbuf);
        if (MAIL_OK != req) throw req;

        sprintf_s(sbuf, MESSAGE_SIZE, "AUTH LOGIN\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;

        memset(rbuf, 0, BLOCK_SIZE);
        err = _recv(s, rbuf, BLOCK_SIZE);
        if (err != 0) throw err;
        
        req = _request(rbuf);
        if (AUTH_OK == req) 
        {
            char line[32], line64[44];             
            u_int len;

            try
            {    // user
                memset(line, 0, 32);
                memset(line64, 0, 44);

                strcpy_s(line, 32, "user");
                len = (u_int)strlen(line);
            
                quot = len / 3;            
                if (len % 3 != 0) quot++;            
                encode64(line, line64, len);
    
                sprintf_s(sbuf, MESSAGE_SIZE, "%s\r\n", line64);
                err = _send(s, sbuf, strlen(sbuf));
                if (err != 0) throw err;

                memset(rbuf, 0, BLOCK_SIZE);
                err = _recv(s, rbuf, BLOCK_SIZE);
                if (err != 0) throw err;
        
                req = _request(rbuf);
                if (AUTH_OK != req) throw req;
                // password
                memset(line, 0, 32);
                memset(line64, 0, 44);

                strcpy_s(line, 32, "login");
                len = (u_int)strlen(line);
            
                quot = len / 3;            
                if (len % 3 != 0) quot++;            
                encode64(line, line64, len);
    
                sprintf_s(sbuf, MESSAGE_SIZE, "%s\r\n", line64);
                err = _send(s, sbuf, strlen(sbuf));
                if (err != 0) throw err;

                memset(rbuf, 0, BLOCK_SIZE);
                err = _recv(s, rbuf, BLOCK_SIZE);
                if (err != 0) throw err;
        
                req = _request(rbuf);
                if (SPASS_OK != req) throw req;
            }        
            catch (int)
            {
            }
        }

        sprintf_s(sbuf, MESSAGE_SIZE, "MAIL FROM: <%s>\r\n", "mail@rambler.ru");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;

        memset(rbuf, 0, BLOCK_SIZE);
        err = _recv(s, rbuf, BLOCK_SIZE);
        if (err != 0) throw err;

        req = _request(rbuf);
        if (MAIL_OK != req) throw req;

        sprintf_s(sbuf, MESSAGE_SIZE, "RCPT TO: <%s>\r\n", "mail@rambler.ru");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;

        memset(rbuf, 0, BLOCK_SIZE);
        err = _recv(s, rbuf, BLOCK_SIZE);
        if (err != 0) throw err;

        req = _request(rbuf);
        if (MAIL_OK != req) throw req;
        // DATA part
        sprintf_s(sbuf, MESSAGE_SIZE, "DATA\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;

        memset(rbuf, 0, BLOCK_SIZE);
        err = _recv(s, rbuf, BLOCK_SIZE);
        if (err != 0) throw err;

        req = _request(rbuf);
        if (DATA_OK != req) throw req;
        
        sprintf_s(sbuf, MESSAGE_SIZE, "From: %s\r\n", "mail@tatem.ru");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;
        
        sprintf_s(sbuf, MESSAGE_SIZE, "X-Priority: 2\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;
        
        sprintf_s(sbuf, MESSAGE_SIZE, "To: %s\r\n", "mail@rambler.ru");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;

        sprintf_s(sbuf, MESSAGE_SIZE, "Subject: Probe\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;
        // MIME part
        sprintf_s(sbuf, MESSAGE_SIZE, "MIME-Version: 1.0\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;
        
        sprintf_s(sbuf, MESSAGE_SIZE, "Content-Type: multipart/mixed;\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;

        sprintf_s(sbuf, MESSAGE_SIZE, " boundary=\"%s\"\r\n", BOUNDARY);     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;

        sprintf_s(sbuf, MESSAGE_SIZE, "\r\n--%s\r\n", BOUNDARY);     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;

        sprintf_s(sbuf, MESSAGE_SIZE, "Content-Type: text/plain; charset=Windows-1251\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;

        sprintf_s(sbuf, MESSAGE_SIZE, "Content-Transfer-Encoding: 8bit\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;        
        // end of MIME part
        sprintf_s(sbuf, MESSAGE_SIZE, "\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;        

        sprintf_s(sbuf, MESSAGE_SIZE, "First line\r\n");        
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;        
        
        sprintf_s(sbuf, MESSAGE_SIZE, "--%s\r\n", BOUNDARY);     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;        

        sprintf_s(sbuf, MESSAGE_SIZE, "Content-Type: application/x-msdownload; name=\"%s\"\r\n", name);     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;        

        sprintf_s(sbuf, MESSAGE_SIZE, "Content-Transfer-Encoding: base64\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;        
        
        sprintf_s(sbuf, MESSAGE_SIZE, "Content-Disposition: attachment; filename=\"%s\"\r\n", name);     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;    

        sprintf_s(sbuf, MESSAGE_SIZE, "\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;            
        
        HANDLE hFile = CreateFile(file, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
        if (INVALID_HANDLE_VALUE != hFile) 
        {                
            try
            {
                char buf[BLOCK_SIZE], buf64[BLOCK_SIZE64];
                DWORD dwBytesRead, i, 
                      size = GetFileSize(hFile, 0);                 
    
                quot = size / BLOCK_SIZE;                    
                rem = size % BLOCK_SIZE;                
                
                for (i = 0; i < quot; i++)
                {
                    memset(buf, 0, BLOCK_SIZE);                    
                    memset(buf64, 0, BLOCK_SIZE64);                        
                    
                    ReadFile(hFile, buf, BLOCK_SIZE, &dwBytesRead, 0);            
                    encode64(buf, buf64, BLOCK_SIZE);

                    err = _send(s, buf64, BLOCK_SIZE64);                        
                    if (err != 0) throw err;                    
                }

                if (rem != 0)
                {
                    memset(buf, 0, BLOCK_SIZE);                    
                    memset(buf64, 0, BLOCK_SIZE64);        

                    ReadFile(hFile, buf, rem, &dwBytesRead, 0);    
                    encode64(buf, buf64, rem);                    

                    quot = rem / 3;                    
                    if (rem % 3 != 0) quot++; 
    
                    err = _send(s, buf64, quot << 2);    
                    if (err != 0) throw err;
                }
            }
            catch (int)
            {
            }            
            CloseHandle(hFile);                                
        }
        
        sprintf_s(sbuf, MESSAGE_SIZE, "\r\n--%s--\r\n", BOUNDARY);     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;    

        sprintf_s(sbuf, MESSAGE_SIZE, ".\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;    

        memset(rbuf, 0, BLOCK_SIZE);
        err = _recv(s, rbuf, BLOCK_SIZE);
        if (err != 0) throw err;

        req = _request(rbuf);
        if (MAIL_OK != req) throw req;
        
        sprintf_s(sbuf, MESSAGE_SIZE, "QUIT\r\n");     
        err = _send(s, sbuf, strlen(sbuf));
        if (err != 0) throw err;    

        memset(rbuf, 0, BLOCK_SIZE);
        err = _recv(s, rbuf, BLOCK_SIZE);
        if (err != 0) throw err;        

        req = _request(rbuf);
        if (SQUIT_OK != req) throw req;
    }
    catch (int e)
    {
        res = e;
    }
    shutdown(s, SD_BOTH);
    closesocket(s);

    return res;
}


Спасибо.


15.08.07 18:42: Перенесено модератором из 'C/C++' — Кодт
Re: отправка писем с аттачем из лвс с kerio
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 15.08.07 22:49
Оценка:
Здравствуйте, Black_Moon, Вы писали:

B_M>Написал smtp клиента


Код некорректен, не полностью описан (пропущены существенные моменты), и зверски неоптимален.
_recv() читает просто приходящий кусок или строку? Если первое, он некорректен. Если второе, он или резко неоптимален (читает по байту), или использует секретный буфер, недокументированный в показанном коде.
Зачем сокету выставлялся nonblocking, если это не используется? Если используется, где задание таймаутов?
Почему размер буфера для одной команды равен MESSAGE_SIZE? При чём тут message?
Зачем столько раз повторять код отсылки заголовков по одному?
Зачем вызывать strlen сразу после sprintf_s?
Добавляет ли encode64() нужные \r\n?
Чему равен BLOCK_SIZE? Значение выше 57 — ошибка. И файл лучше читать через stdio, чтобы не делать лишние сисколлы.
Зачем реагировать на ошибки от QUIT?

Итого. За код — твёрдая двойка. Копать дальше не хочу. "Ваши крокодилы — вы и спасайте"
The God is real, unless declared integer.
Re[2]: отправка писем с аттачем из лвс с kerio
От: Black_Moon  
Дата: 16.08.07 08:05
Оценка:
Здравствуйте, netch80, Вы писали:

N>Здравствуйте, Black_Moon, Вы писали:


B_M>>Написал smtp клиента


N>Код некорректен, не полностью описан (пропущены существенные моменты), и зверски неоптимален.

N>_recv() читает просто приходящий кусок или строку? Если первое, он некорректен. Если второе, он или резко неоптимален (читает по байту), или использует секретный буфер, недокументированный в показанном коде.
N>Зачем сокету выставлялся nonblocking, если это не используется? Если используется, где задание таймаутов?
N>Почему размер буфера для одной команды равен MESSAGE_SIZE? При чём тут message?
N>Зачем столько раз повторять код отсылки заголовков по одному?
N>Зачем вызывать strlen сразу после sprintf_s?
N>Добавляет ли encode64() нужные \r\n?
N>Чему равен BLOCK_SIZE? Значение выше 57 — ошибка. И файл лучше читать через stdio, чтобы не делать лишние сисколлы.
N>Зачем реагировать на ошибки от QUIT?

N>Итого. За код — твёрдая двойка. Копать дальше не хочу. "Ваши крокодилы — вы и спасайте"



N>_recv() читает просто приходящий кусок или строку

_recv() читает все что приходит от сервера за один заход (просто стоит ограничение на буфер — BLOCK_SIZE = 1023)

N>Зачем сокету выставлялся nonblocking, если это не используется? Если используется, где задание таймаутов?

сокет non-blocking — так как все _connect(), _send() и _recv() используют select с таймаутами (просто код не приведен).
N>Почему размер буфера для одной команды равен MESSAGE_SIZE? При чём тут message?
Дело в том что я использую 2 буфера — один для отправки сообщений — 2й для получения ответа, соотв у них разные размеры.

N>Зачем столько раз повторять код отсылки заголовков по одному?

Зачем реагировать на ошибки от QUIT?
Тут верно — лишнее написано.

N>Чему равен BLOCK_SIZE? Значение выше 57 — ошибка.

Здесь можно поподробнее — откуда такая цифка 57.

Спасибо.
Re[3]: отправка писем с аттачем из лвс с kerio
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 16.08.07 09:12
Оценка:
Здравствуйте, Black_Moon, Вы писали:

N>>_recv() читает просто приходящий кусок или строку

B_M>_recv() читает все что приходит от сервера за один заход (просто стоит ограничение на буфер — BLOCK_SIZE = 1023)

Значит, некорректно. Потому что сервер может послать, например, в первой порции данных "220 zuka.buka.com", а второй дополнить строку — " ESMTP ready\r\n". Вы это не обработаете. Тем более это с EHLO.
Корректным вариантом будет:
1. Вести буфер текущего принятого и дописывать новые поступления в него. Желательный размер буфера — не менее 1024 символа.
2. Цикл должен быть устроен так: читаем вход; если есть данные, дописываем к буферу, иначе снова ждём; если в буфере увидели \n — значит, набралась строка и её надо обработать, после чего удалить из буфера (учитывая, что после этой строки тоже что-то могло прийти); если буфер переполнен, а \n нету — выйти с ошибкой.
И только когда строка набралась или случился таймаут или буфер переполнен — обрабатывать ситуацию.

N>>Почему размер буфера для одной команды равен MESSAGE_SIZE? При чём тут message?

B_M>Дело в том что я использую 2 буфера — один для отправки сообщений — 2й для получения ответа, соотв у них разные размеры.
По названию константы это не видно.

N>>Чему равен BLOCK_SIZE? Значение выше 57 — ошибка.

B_M>Здесь можно поподробнее — откуда такая цифка 57.

RFC2045 пункт 6.8:

<cite>
The encoded output stream must be represented in lines of no more than 76 characters each.
</cite>

Раз 76 целевых после трансляции в base64 — значит, 57 до этого.
Так что или менять значение, или использовать другую константу.

B_M>Спасибо.
The God is real, unless declared integer.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.