1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "str.h"
5 #include "array.h"
6 #include "smtp-syntax.h"
7
8 #include "smtp-server-private.h"
9
10 /* EHLO, HELO commands */
11
12 static void
cmd_helo_completed(struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_helo * data)13 cmd_helo_completed(struct smtp_server_cmd_ctx *cmd,
14 struct smtp_server_cmd_helo *data)
15 {
16 struct smtp_server_connection *conn = cmd->conn;
17 struct smtp_server_command *command = cmd->cmd;
18
19 i_assert(smtp_server_command_is_replied(command));
20 if (!smtp_server_command_replied_success(command)) {
21 /* Failure */
22 return;
23 }
24
25 if (conn->pending_helo == &data->helo)
26 conn->pending_helo = NULL;
27
28 /* Success */
29 smtp_server_connection_reset_state(conn);
30
31 i_free(conn->helo_domain);
32 conn->helo_domain = i_strdup(data->helo.domain);
33 conn->helo.domain = conn->helo_domain;
34 conn->helo.domain_valid = data->helo.domain_valid;
35 conn->helo.old_smtp = data->helo.old_smtp;
36 }
37
38 static void
cmd_helo_next(struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_helo * data)39 cmd_helo_next(struct smtp_server_cmd_ctx *cmd,
40 struct smtp_server_cmd_helo *data)
41 {
42 struct smtp_server_connection *conn = cmd->conn;
43
44 if (conn->helo.domain == NULL ||
45 strcmp(conn->helo.domain, data->helo.domain) != 0 ||
46 conn->helo.old_smtp != data->helo.old_smtp)
47 data->changed = TRUE; /* Definitive assessment */
48 }
49
50 static void
smtp_server_cmd_helo_run(struct smtp_server_cmd_ctx * cmd,const char * params,bool old_smtp)51 smtp_server_cmd_helo_run(struct smtp_server_cmd_ctx *cmd, const char *params,
52 bool old_smtp)
53 {
54 struct smtp_server_connection *conn = cmd->conn;
55 const struct smtp_server_callbacks *callbacks = conn->callbacks;
56 struct smtp_server_cmd_helo *helo_data;
57 struct smtp_server_command *command = cmd->cmd;
58 bool first = (conn->pending_helo == NULL && conn->helo.domain == NULL);
59 const char *domain = NULL;
60 int ret;
61
62 /* Parse domain argument */
63
64 if (*params == '\0') {
65 smtp_server_reply(cmd, 501, "", "Missing hostname");
66 return;
67 }
68 ret = smtp_helo_domain_parse(params, !old_smtp, &domain);
69
70 smtp_server_command_input_lock(cmd);
71 if (conn->state.state == SMTP_SERVER_STATE_GREETING) {
72 smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_HELO,
73 NULL);
74 }
75
76 helo_data = p_new(cmd->pool, struct smtp_server_cmd_helo, 1);
77 helo_data->helo.domain = p_strdup(cmd->pool, domain);
78 helo_data->helo.domain_valid = ( ret >= 0 );
79 helo_data->helo.old_smtp = old_smtp;
80 helo_data->first = first;
81 command->data = helo_data;
82
83 if (conn->helo.domain == NULL ||
84 (domain != NULL && strcmp(conn->helo.domain, domain) != 0) ||
85 conn->helo.old_smtp != old_smtp)
86 helo_data->changed = TRUE; /* Preliminary assessment */
87
88 if (conn->pending_helo == NULL)
89 conn->pending_helo = &helo_data->helo;
90
91 smtp_server_command_add_hook(
92 command, SMTP_SERVER_COMMAND_HOOK_NEXT,
93 cmd_helo_next, helo_data);
94 smtp_server_command_add_hook(
95 command, SMTP_SERVER_COMMAND_HOOK_COMPLETED,
96 cmd_helo_completed, helo_data);
97
98 smtp_server_command_ref(command);
99 if (callbacks != NULL && callbacks->conn_cmd_helo != NULL) {
100 /* Specific implementation of EHLO command */
101 ret = callbacks->conn_cmd_helo(conn->context, cmd, helo_data);
102 if (ret <= 0) {
103 i_assert(ret == 0 ||
104 smtp_server_command_is_replied(command));
105 /* Command is waiting for external event or it failed */
106 smtp_server_command_unref(&command);
107 return;
108 }
109 }
110
111 if (!smtp_server_command_is_replied(command)) {
112 /* Submit default EHLO reply if none is provided */
113 smtp_server_cmd_ehlo_reply_default(cmd);
114 }
115 smtp_server_command_unref(&command);
116 }
117
smtp_server_cmd_ehlo(struct smtp_server_cmd_ctx * cmd,const char * params)118 void smtp_server_cmd_ehlo(struct smtp_server_cmd_ctx *cmd, const char *params)
119 {
120 /* ehlo = "EHLO" SP ( Domain / address-literal ) CRLF */
121
122 smtp_server_cmd_helo_run(cmd, params, FALSE);
123 }
124
smtp_server_cmd_helo(struct smtp_server_cmd_ctx * cmd,const char * params)125 void smtp_server_cmd_helo(struct smtp_server_cmd_ctx *cmd, const char *params)
126 {
127 /* helo = "HELO" SP Domain CRLF */
128
129 smtp_server_cmd_helo_run(cmd, params, TRUE);
130 }
131
132 struct smtp_server_reply *
smtp_server_cmd_ehlo_reply_create(struct smtp_server_cmd_ctx * cmd)133 smtp_server_cmd_ehlo_reply_create(struct smtp_server_cmd_ctx *cmd)
134 {
135 static struct {
136 const char *name;
137 void (*add)(struct smtp_server_reply *reply);
138 } standard_caps[] = {
139 /* Sorted alphabetically */
140 { "8BITMIME", smtp_server_reply_ehlo_add_8bitmime },
141 { "BINARYMIME", smtp_server_reply_ehlo_add_binarymime },
142 { "CHUNKING", smtp_server_reply_ehlo_add_chunking },
143 { "DSN", smtp_server_reply_ehlo_add_dsn },
144 { "ENHANCEDSTATUSCODES",
145 smtp_server_reply_ehlo_add_enhancedstatuscodes },
146 { "PIPELINING", smtp_server_reply_ehlo_add_pipelining },
147 { "SIZE", smtp_server_reply_ehlo_add_size },
148 { "STARTTLS", smtp_server_reply_ehlo_add_starttls },
149 { "VRFY", smtp_server_reply_ehlo_add_vrfy },
150 { "XCLIENT", smtp_server_reply_ehlo_add_xclient }
151 };
152 const unsigned int standard_caps_count = N_ELEMENTS(standard_caps);
153 struct smtp_server_connection *conn = cmd->conn;
154 struct smtp_server_command *command = cmd->cmd;
155 struct smtp_server_cmd_helo *helo_data = command->data;
156 const struct smtp_capability_extra *extra_caps = NULL;
157 unsigned int extra_caps_count, i, j;
158 struct smtp_server_reply *reply;
159
160 reply = smtp_server_reply_create_ehlo(cmd->cmd);
161
162 if (helo_data->helo.old_smtp) {
163 i_assert(cmd->cmd->reg->func == smtp_server_cmd_helo);
164 return reply;
165 }
166 i_assert(cmd->cmd->reg->func == smtp_server_cmd_ehlo);
167
168 extra_caps_count = 0;
169 if (array_is_created(&conn->extra_capabilities)) {
170 extra_caps = array_get(&conn->extra_capabilities,
171 &extra_caps_count);
172 }
173
174 i = j = 0;
175 while (i < standard_caps_count || j < extra_caps_count) {
176 if (i < standard_caps_count &&
177 (j >= extra_caps_count ||
178 strcasecmp(standard_caps[i].name,
179 extra_caps[j].name) < 0)) {
180 standard_caps[i].add(reply);
181 i++;
182 } else {
183 smtp_server_reply_ehlo_add_params(
184 reply, extra_caps[j].name,
185 extra_caps[j].params);
186 j++;
187 }
188 }
189 return reply;
190 }
191
smtp_server_cmd_ehlo_reply_default(struct smtp_server_cmd_ctx * cmd)192 void smtp_server_cmd_ehlo_reply_default(struct smtp_server_cmd_ctx *cmd)
193 {
194 struct smtp_server_reply *reply;
195
196 reply = smtp_server_cmd_ehlo_reply_create(cmd);
197 smtp_server_reply_submit(reply);
198 }
199