Simple socket smtp client

I want to build a simple smtp client.
Here is what I have so far.
I'm stuck at AUTH LOGIN - where I'm getting 500 5.5.1 Invalid command

It works from the commandline so there is something messed up.

Please help me to get to the correct direction

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <stdio.h>
#include <string>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

class Smtp {
 
private:
    struct sockaddr_in serv_addr;
    struct hostent *server;
    
private:
    
    void empty_buffer(char buffer[256])
    {
        bzero(buffer,256);
    }
    
    int create_socket()
    {
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
            std::cout << "ERROR opening socket";
            exit(0);
        }
        return sockfd;
    }
    
    
    void connect_socket(int sockfd)
    {
        if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) {
            std::cout << "ERROR connecting";
            exit(0);
        }
    }
    
    ssize_t read_to_buffer(int sockfd, char buffer[256])
    {
        ssize_t res = read(sockfd,buffer,255);
        if (res < 0) {
            std::cout << "ERROR reading from socket";
            exit(0);
        }
        return res;
    }
    
    bool check_handshake(char buffer[256])
    {
        char greeting [3];
        std::memcpy(greeting, buffer, 3);
        std::string greeting_str(greeting);
        
        // https://tools.ietf.org/html/rfc5321#section-3.1 - Return 220 as OK
        const std::string ok_code = "220";
        if (greeting_str != ok_code) {
            std::cout << "Server does not respond with OK";
            return false;
            
        }
        return true;
    }
    
    void log_state(char buffer[256])
    {
        printf("%s\n", buffer);
    }
    
    
    ssize_t send_command(std::string command, int sockfd, char buffer[256], ssize_t n)
    {
        command += '\n';
        empty_buffer(buffer);
        char command_buffer[255];
        strcpy(command_buffer, command.c_str());
        n = write(sockfd, command_buffer, sizeof(command_buffer));
        if (n < 0) {
            std::cout << "ERROR writing to socket";
            exit(0);
        }
        n = read_to_buffer(sockfd, buffer);
        log_state(buffer);
        std::cout << "::::" << std::endl;
        return n;
    }
    

    
public:
    void new_connection(std::string domain, int port_no) {
        ssize_t n;
        int sockfd;
        
        char buffer[256];
        
        sockfd = create_socket();
        server = gethostbyname("smtp.mailtrap.io");
        serv_addr.sin_family = AF_INET;
        bcopy((char *)this->server->h_addr, (char *)&this->serv_addr.sin_addr.s_addr, server->h_length);
        serv_addr.sin_port = htons(port_no);
        
        
        connect_socket(sockfd);
        empty_buffer(buffer);
        
        n = read_to_buffer(sockfd, buffer);
        
        if (!check_handshake(buffer))
        {
            exit(0);
        }
        
        if (n < 0)
        {
            std::cout << "ERROR reading from socket";
        }
        log_state(buffer);
        
        n = send_command("EHLO 127.0.0.1", sockfd, buffer, n);
        n = send_command("AUTH LOGIN", sockfd, buffer, n);
       
        close(sockfd);
    }

};
Last edited on
https://tools.ietf.org/html/rfc5321#section-4.2
In particular, the 220, 221, 251, 421, and 551 reply codes are associated with message text that must be parsed and interpreted by machines.

You're not stripping out the reply code.
1
2
3
4
5
6
7
        const std::string ok_code = "220";
        if (greeting_str != ok_code) {
            std::cout << "Server does not respond with OK";
            return false;
            
        }
        return true;


Also: Section 4.1.1.1, Extended HELLO
https://tools.ietf.org/html/rfc5321#section-4.1.1
Lines are terminated with CRLF. You're just sending LF.
1
2
3
    ssize_t send_command(std::string command, int sockfd, char buffer[256], ssize_t n)
    {
        command += '\n';


Sendmail is ok with LF, but newer servers aren't, as I recall.
Last edited on
Registered users can post here. Sign in or register to post.