Mercurial > hg > nginx
comparison src/mail/ngx_mail_smtp_handler.c @ 1476:67578e966dcc
split pop3, imap, and smtp handlers
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Thu, 13 Sep 2007 20:13:18 +0000 |
parents | src/mail/ngx_mail_handler.c@32450a2bbdf4 |
children | 59e1caf2be94 |
comparison
equal
deleted
inserted
replaced
1475:732fe8258857 | 1476:67578e966dcc |
---|---|
1 | |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_event.h> | |
10 #include <ngx_mail.h> | |
11 | |
12 | |
13 static ngx_int_t ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c); | |
14 static ngx_int_t ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c); | |
15 static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c); | |
16 static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s, | |
17 ngx_connection_t *c); | |
18 | |
19 | |
20 static u_char smtp_ok[] = "250 2.0.0 OK" CRLF; | |
21 static u_char smtp_bye[] = "221 2.0.0 Bye" CRLF; | |
22 static u_char smtp_next[] = "334 " CRLF; | |
23 static u_char smtp_username[] = "334 VXNlcm5hbWU6" CRLF; | |
24 static u_char smtp_password[] = "334 UGFzc3dvcmQ6" CRLF; | |
25 static u_char smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF; | |
26 static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF; | |
27 static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF; | |
28 | |
29 | |
30 void | |
31 ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c) | |
32 { | |
33 ngx_mail_core_srv_conf_t *cscf; | |
34 | |
35 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
36 | |
37 if (cscf->smtp_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) { | |
38 if (ngx_mail_salt(s, c, cscf) != NGX_OK) { | |
39 ngx_mail_session_internal_server_error(s); | |
40 return; | |
41 } | |
42 } | |
43 | |
44 c->read->handler = ngx_mail_smtp_init_protocol; | |
45 | |
46 s->out = cscf->smtp_greeting; | |
47 | |
48 ngx_mail_send(c->write); | |
49 } | |
50 | |
51 | |
52 void | |
53 ngx_mail_smtp_init_protocol(ngx_event_t *rev) | |
54 { | |
55 ngx_connection_t *c; | |
56 ngx_mail_session_t *s; | |
57 | |
58 c = rev->data; | |
59 | |
60 c->log->action = "in auth state"; | |
61 | |
62 if (rev->timedout) { | |
63 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); | |
64 c->timedout = 1; | |
65 ngx_mail_close_connection(c); | |
66 return; | |
67 } | |
68 | |
69 s = c->data; | |
70 | |
71 if (s->buffer == NULL) { | |
72 if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) | |
73 == NGX_ERROR) | |
74 { | |
75 ngx_mail_session_internal_server_error(s); | |
76 return; | |
77 } | |
78 | |
79 s->buffer = ngx_create_temp_buf(c->pool, 512); | |
80 if (s->buffer == NULL) { | |
81 ngx_mail_session_internal_server_error(s); | |
82 return; | |
83 } | |
84 } | |
85 | |
86 s->mail_state = ngx_smtp_start; | |
87 c->read->handler = ngx_smtp_auth_state; | |
88 | |
89 ngx_smtp_auth_state(rev); | |
90 } | |
91 | |
92 | |
93 void | |
94 ngx_smtp_auth_state(ngx_event_t *rev) | |
95 { | |
96 ngx_int_t rc; | |
97 ngx_connection_t *c; | |
98 ngx_mail_session_t *s; | |
99 | |
100 c = rev->data; | |
101 s = c->data; | |
102 | |
103 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state"); | |
104 | |
105 if (rev->timedout) { | |
106 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); | |
107 c->timedout = 1; | |
108 ngx_mail_close_connection(c); | |
109 return; | |
110 } | |
111 | |
112 if (s->out.len) { | |
113 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy"); | |
114 s->blocked = 1; | |
115 return; | |
116 } | |
117 | |
118 s->blocked = 0; | |
119 | |
120 rc = ngx_mail_read_command(s); | |
121 | |
122 if (rc == NGX_AGAIN || rc == NGX_ERROR) { | |
123 return; | |
124 } | |
125 | |
126 s->out.len = sizeof(smtp_ok) - 1; | |
127 s->out.data = smtp_ok; | |
128 | |
129 if (rc == NGX_OK) { | |
130 switch (s->mail_state) { | |
131 | |
132 case ngx_smtp_start: | |
133 | |
134 switch (s->command) { | |
135 | |
136 case NGX_SMTP_HELO: | |
137 case NGX_SMTP_EHLO: | |
138 rc = ngx_mail_smtp_helo(s, c); | |
139 break; | |
140 | |
141 case NGX_SMTP_AUTH: | |
142 rc = ngx_mail_smtp_auth(s, c); | |
143 break; | |
144 | |
145 case NGX_SMTP_QUIT: | |
146 s->quit = 1; | |
147 s->out.len = sizeof(smtp_bye) - 1; | |
148 s->out.data = smtp_bye; | |
149 break; | |
150 | |
151 case NGX_SMTP_MAIL: | |
152 rc = ngx_mail_smtp_mail(s, c); | |
153 break; | |
154 | |
155 case NGX_SMTP_NOOP: | |
156 case NGX_SMTP_RSET: | |
157 break; | |
158 | |
159 case NGX_SMTP_STARTTLS: | |
160 rc = ngx_mail_smtp_starttls(s, c); | |
161 break; | |
162 | |
163 default: | |
164 rc = NGX_MAIL_PARSE_INVALID_COMMAND; | |
165 break; | |
166 } | |
167 | |
168 break; | |
169 | |
170 case ngx_smtp_auth_login_username: | |
171 rc = ngx_mail_auth_login_username(s, c); | |
172 | |
173 s->out.len = sizeof(smtp_password) - 1; | |
174 s->out.data = smtp_password; | |
175 s->mail_state = ngx_smtp_auth_login_password; | |
176 break; | |
177 | |
178 case ngx_smtp_auth_login_password: | |
179 rc = ngx_mail_auth_login_password(s, c); | |
180 break; | |
181 | |
182 case ngx_smtp_auth_plain: | |
183 rc = ngx_mail_auth_plain(s, c, 0); | |
184 break; | |
185 | |
186 case ngx_smtp_auth_cram_md5: | |
187 rc = ngx_mail_auth_cram_md5(s, c); | |
188 break; | |
189 } | |
190 } | |
191 | |
192 switch (rc) { | |
193 | |
194 case NGX_DONE: | |
195 ngx_mail_auth(s); | |
196 return; | |
197 | |
198 case NGX_ERROR: | |
199 ngx_mail_session_internal_server_error(s); | |
200 return; | |
201 | |
202 case NGX_MAIL_PARSE_INVALID_COMMAND: | |
203 s->mail_state = ngx_smtp_start; | |
204 s->state = 0; | |
205 | |
206 s->out.len = sizeof(smtp_invalid_command) - 1; | |
207 s->out.data = smtp_invalid_command; | |
208 | |
209 /* fall through */ | |
210 | |
211 case NGX_OK: | |
212 s->args.nelts = 0; | |
213 s->buffer->pos = s->buffer->start; | |
214 s->buffer->last = s->buffer->start; | |
215 | |
216 if (s->state) { | |
217 s->arg_start = s->buffer->start; | |
218 } | |
219 | |
220 ngx_mail_send(c->write); | |
221 } | |
222 } | |
223 | |
224 | |
225 static ngx_int_t | |
226 ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c) | |
227 { | |
228 ngx_str_t *arg; | |
229 ngx_mail_core_srv_conf_t *cscf; | |
230 #if (NGX_MAIL_SSL) | |
231 ngx_mail_ssl_conf_t *sslcf; | |
232 #endif | |
233 | |
234 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
235 | |
236 if (s->args.nelts != 1) { | |
237 s->out.len = sizeof(smtp_invalid_argument) - 1; | |
238 s->out.data = smtp_invalid_argument; | |
239 s->state = 0; | |
240 return NGX_OK; | |
241 } | |
242 | |
243 arg = s->args.elts; | |
244 | |
245 s->smtp_helo.len = arg[0].len; | |
246 | |
247 s->smtp_helo.data = ngx_palloc(c->pool, arg[0].len); | |
248 if (s->smtp_helo.data == NULL) { | |
249 return NGX_ERROR; | |
250 } | |
251 | |
252 ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len); | |
253 | |
254 if (s->command == NGX_SMTP_HELO) { | |
255 s->out = cscf->smtp_server_name; | |
256 | |
257 } else { | |
258 s->esmtp = 1; | |
259 | |
260 #if (NGX_MAIL_SSL) | |
261 | |
262 if (c->ssl == NULL) { | |
263 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); | |
264 | |
265 if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { | |
266 s->out = cscf->smtp_starttls_capability; | |
267 return NGX_OK; | |
268 } | |
269 | |
270 if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { | |
271 s->out = cscf->smtp_starttls_only_capability; | |
272 return NGX_OK; | |
273 } | |
274 } | |
275 #endif | |
276 | |
277 s->out = cscf->smtp_capability; | |
278 } | |
279 | |
280 return NGX_OK; | |
281 } | |
282 | |
283 | |
284 static ngx_int_t | |
285 ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c) | |
286 { | |
287 u_char *p; | |
288 ngx_str_t *arg, salt; | |
289 ngx_uint_t n; | |
290 ngx_mail_core_srv_conf_t *cscf; | |
291 #if (NGX_MAIL_SSL) | |
292 ngx_mail_ssl_conf_t *sslcf; | |
293 | |
294 if (c->ssl == NULL) { | |
295 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); | |
296 | |
297 if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { | |
298 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
299 } | |
300 } | |
301 | |
302 #endif | |
303 | |
304 if (s->args.nelts == 0) { | |
305 s->out.len = sizeof(smtp_invalid_argument) - 1; | |
306 s->out.data = smtp_invalid_argument; | |
307 s->state = 0; | |
308 return NGX_OK; | |
309 } | |
310 | |
311 if (s->args.nelts != 1) { | |
312 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
313 } | |
314 | |
315 arg = s->args.elts; | |
316 | |
317 if (arg[0].len == 5) { | |
318 | |
319 if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) { | |
320 | |
321 if (s->args.nelts != 1) { | |
322 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
323 } | |
324 | |
325 s->out.len = sizeof(smtp_username) - 1; | |
326 s->out.data = smtp_username; | |
327 s->mail_state = ngx_smtp_auth_login_username; | |
328 | |
329 return NGX_OK; | |
330 | |
331 } else if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) { | |
332 | |
333 s->out.len = sizeof(smtp_next) - 1; | |
334 s->out.data = smtp_next; | |
335 s->mail_state = ngx_smtp_auth_plain; | |
336 | |
337 return NGX_OK; | |
338 } | |
339 | |
340 } else if (arg[0].len == 8 | |
341 && ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) | |
342 { | |
343 if (s->args.nelts != 1) { | |
344 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
345 } | |
346 | |
347 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
348 | |
349 if (!(cscf->smtp_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { | |
350 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
351 } | |
352 | |
353 p = ngx_palloc(c->pool, | |
354 sizeof("334 " CRLF) - 1 | |
355 + ngx_base64_encoded_length(s->salt.len)); | |
356 if (p == NULL) { | |
357 return NGX_ERROR; | |
358 } | |
359 | |
360 p[0] = '3'; p[1]= '3'; p[2] = '4'; p[3]= ' '; | |
361 salt.data = &p[4]; | |
362 s->salt.len -= 2; | |
363 | |
364 ngx_encode_base64(&salt, &s->salt); | |
365 | |
366 s->salt.len += 2; | |
367 n = 4 + salt.len; | |
368 p[n++] = CR; p[n++] = LF; | |
369 | |
370 s->out.len = n; | |
371 s->out.data = p; | |
372 s->mail_state = ngx_smtp_auth_cram_md5; | |
373 | |
374 return NGX_OK; | |
375 } | |
376 | |
377 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
378 } | |
379 | |
380 | |
381 static ngx_int_t | |
382 ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c) | |
383 { | |
384 u_char ch; | |
385 ngx_str_t mail; | |
386 ngx_uint_t i; | |
387 | |
388 if (c->log->log_level >= NGX_LOG_INFO) { | |
389 mail.len = s->buffer->last - s->buffer->start; | |
390 mail.data = s->buffer->start; | |
391 | |
392 for (i = 0; i < mail.len; i++) { | |
393 ch = mail.data[i]; | |
394 | |
395 if (ch != CR && ch != LF) { | |
396 continue; | |
397 } | |
398 | |
399 mail.data[i] = ' '; | |
400 } | |
401 | |
402 while (i) { | |
403 if (mail.data[i - 1] != ' ') { | |
404 break; | |
405 } | |
406 | |
407 i--; | |
408 } | |
409 | |
410 mail.len = i; | |
411 | |
412 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, | |
413 "client was rejected: \"%V\"", &mail); | |
414 } | |
415 | |
416 s->out.len = sizeof(smtp_auth_required) - 1; | |
417 s->out.data = smtp_auth_required; | |
418 | |
419 return NGX_OK; | |
420 } | |
421 | |
422 | |
423 static ngx_int_t | |
424 ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c) | |
425 { | |
426 #if (NGX_MAIL_SSL) | |
427 ngx_mail_ssl_conf_t *sslcf; | |
428 | |
429 if (c->ssl == NULL) { | |
430 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); | |
431 if (sslcf->starttls) { | |
432 | |
433 /* | |
434 * RFC3207 requires us to discard any knowledge | |
435 * obtained from client before STARTTLS. | |
436 */ | |
437 | |
438 s->smtp_helo.len = 0; | |
439 s->smtp_helo.data = NULL; | |
440 | |
441 c->read->handler = ngx_mail_starttls_handler; | |
442 return NGX_OK; | |
443 } | |
444 } | |
445 | |
446 #endif | |
447 | |
448 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
449 } |