masqmail

annotate src/smtp_out.c @ 222:8cddc65765bd

added support for STARTTLS wrappers added the route config option `instant_helo' which causes masqmail, as SMTP client, not to wait for the server's 220 greeting. Instead if says EHLO right at once. You'll need this for STARTTLS wrappers that usually eat the greeting line.
author meillo@marmaro.de
date Fri, 23 Jul 2010 10:57:53 +0200
parents 10da50168dab
children 996b53a50f55
rev   line source
meillo@0 1 /* smtp_out.c, Copyright (C) 1999-2001 Oliver Kurth,
meillo@0 2 *
meillo@0 3 * This program is free software; you can redistribute it and/or modify
meillo@0 4 * it under the terms of the GNU General Public License as published by
meillo@0 5 * the Free Software Foundation; either version 2 of the License, or
meillo@0 6 * (at your option) any later version.
meillo@10 7 *
meillo@0 8 * This program is distributed in the hope that it will be useful,
meillo@0 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@0 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@0 11 * GNU General Public License for more details.
meillo@0 12 *
meillo@0 13 * You should have received a copy of the GNU General Public License
meillo@0 14 * along with this program; if not, write to the Free Software
meillo@0 15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
meillo@0 16 */
meillo@0 17
meillo@0 18 /*
meillo@0 19 send bugs to: kurth@innominate.de
meillo@0 20 */
meillo@0 21
meillo@0 22 /*
meillo@0 23 I always forget these rfc numbers:
meillo@0 24 RFC 821 (SMTP)
meillo@0 25 RFC 1869 (ESMTP)
meillo@0 26 RFC 1870 (ESMTP SIZE)
meillo@0 27 RFC 2197 (ESMTP PIPELINE)
meillo@0 28 RFC 2554 (ESMTP AUTH)
meillo@0 29 */
meillo@0 30
meillo@0 31 #include "masqmail.h"
meillo@0 32 #include "smtp_out.h"
meillo@0 33 #include "readsock.h"
meillo@0 34
meillo@0 35 #ifdef ENABLE_AUTH
meillo@0 36
meillo@0 37 #ifdef USE_LIB_CRYPTO
meillo@0 38 #include <openssl/hmac.h>
meillo@0 39 #include <openssl/md5.h>
meillo@0 40 #include <openssl/evp.h>
meillo@0 41 #else
meillo@0 42 #include "md5/md5.h"
meillo@0 43 #include "md5/hmac_md5.h"
meillo@0 44 #endif
meillo@0 45
meillo@0 46 #include "base64/base64.h"
meillo@0 47 #endif
meillo@0 48
meillo@10 49 void
meillo@10 50 destroy_smtpbase(smtp_base * psb)
meillo@0 51 {
meillo@10 52 fclose(psb->in);
meillo@10 53 fclose(psb->out);
meillo@0 54
meillo@10 55 close(psb->sock);
meillo@0 56
meillo@10 57 if (psb->helo_name)
meillo@10 58 g_free(psb->helo_name);
meillo@10 59 if (psb->buffer)
meillo@10 60 g_free(psb->buffer);
meillo@10 61 if (psb->auth_names)
meillo@10 62 g_strfreev(psb->auth_names);
meillo@0 63
meillo@10 64 if (psb->auth_name)
meillo@10 65 g_free(psb->auth_name);
meillo@10 66 if (psb->auth_login)
meillo@10 67 g_free(psb->auth_login);
meillo@10 68 if (psb->auth_secret)
meillo@10 69 g_free(psb->auth_secret);
meillo@0 70 }
meillo@0 71
meillo@10 72 gchar*
meillo@10 73 set_heloname(smtp_base * psb, gchar * default_name, gboolean do_correct)
meillo@0 74 {
meillo@10 75 struct sockaddr_in sname;
meillo@10 76 int len = sizeof(struct sockaddr_in);
meillo@10 77 struct hostent *host_entry;
meillo@0 78
meillo@10 79 if (do_correct) {
meillo@10 80 getsockname(psb->sock, (struct sockaddr *) (&sname), &len);
meillo@10 81 DEBUG(5) debugf("socket: name.sin_addr = %s\n", inet_ntoa(sname.sin_addr));
meillo@10 82 host_entry = gethostbyaddr((const char *) &(sname.sin_addr), sizeof(sname.sin_addr), AF_INET);
meillo@10 83 if (host_entry) {
meillo@10 84 psb->helo_name = g_strdup(host_entry->h_name);
meillo@10 85 } else {
meillo@10 86 /* we failed to look up our own name. Instead of giving our local hostname,
meillo@10 87 we may give our IP number to show the server that we are at least
meillo@10 88 willing to be honest. For the really picky ones. */
meillo@10 89 DEBUG(5) debugf("failed to look up own host name.\n");
meillo@10 90 psb->helo_name = g_strdup_printf("[%s]", inet_ntoa(sname.sin_addr));
meillo@10 91 }
meillo@10 92 DEBUG(5) debugf("helo_name = %s\n", psb->helo_name);
meillo@10 93 }
meillo@10 94 if (psb->helo_name == NULL) {
meillo@10 95 psb->helo_name = g_strdup(default_name);
meillo@10 96 }
meillo@10 97 return psb->helo_name;
meillo@10 98 }
meillo@0 99
meillo@0 100 #ifdef ENABLE_AUTH
meillo@0 101
meillo@10 102 gboolean
meillo@10 103 set_auth(smtp_base * psb, gchar * name, gchar * login, gchar * secret)
meillo@0 104 {
meillo@10 105 if ((strcasecmp(name, "CRAM-MD5") == 0) || (strcasecmp(name, "LOGIN") == 0)) {
meillo@10 106 psb->auth_name = g_strdup(name);
meillo@10 107 psb->auth_login = g_strdup(login);
meillo@10 108 psb->auth_secret = g_strdup(secret);
meillo@10 109
meillo@10 110 return TRUE;
meillo@10 111 }
meillo@10 112 return FALSE;
meillo@0 113 }
meillo@0 114
meillo@0 115 #endif
meillo@0 116
meillo@10 117 static smtp_base*
meillo@10 118 create_smtpbase(gint sock)
meillo@0 119 {
meillo@10 120 gint dup_sock;
meillo@0 121
meillo@10 122 smtp_base *psb = (smtp_base *) g_malloc(sizeof(smtp_base));
meillo@0 123
meillo@10 124 psb->sock = sock;
meillo@0 125
meillo@10 126 psb->use_esmtp = FALSE;
meillo@10 127 psb->use_size = FALSE;
meillo@10 128 psb->use_pipelining = FALSE;
meillo@10 129 psb->use_auth = FALSE;
meillo@0 130
meillo@10 131 psb->max_size = 0;
meillo@10 132 psb->auth_names = NULL;
meillo@0 133
meillo@10 134 psb->buffer = (gchar *) g_malloc(SMTP_BUF_LEN);
meillo@0 135
meillo@10 136 dup_sock = dup(sock);
meillo@10 137 psb->out = fdopen(sock, "w");
meillo@10 138 psb->in = fdopen(dup_sock, "r");
meillo@0 139
meillo@10 140 psb->error = smtp_ok;
meillo@0 141
meillo@10 142 psb->helo_name = NULL;
meillo@0 143
meillo@10 144 psb->auth_name = psb->auth_login = psb->auth_secret = NULL;
meillo@10 145
meillo@10 146 return psb;
meillo@0 147 }
meillo@0 148
meillo@10 149 static gboolean
meillo@10 150 read_response(smtp_base * psb, int timeout)
meillo@0 151 {
meillo@10 152 gint buf_pos = 0;
meillo@10 153 gchar code[5];
meillo@10 154 gint i, len;
meillo@0 155
meillo@10 156 do {
meillo@10 157 len = read_sockline(psb->in, &(psb->buffer[buf_pos]), SMTP_BUF_LEN - buf_pos, timeout, READSOCKL_CHUG);
meillo@10 158 if (len == -3) {
meillo@10 159 psb->error = smtp_timeout;
meillo@10 160 return FALSE;
meillo@10 161 } else if (len == -2) {
meillo@10 162 psb->error = smtp_syntax;
meillo@10 163 return FALSE;
meillo@10 164 } else if (len == -1) {
meillo@10 165 psb->error = smtp_eof;
meillo@10 166 return FALSE;
meillo@10 167 }
meillo@10 168 for (i = 0; i < 4; i++)
meillo@10 169 code[i] = psb->buffer[buf_pos + i];
meillo@15 170 code[i] = '\0';
meillo@10 171 psb->last_code = atoi(code);
meillo@0 172
meillo@10 173 buf_pos += len;
meillo@0 174
meillo@10 175 } while (code[3] == '-');
meillo@177 176 if (psb->buffer) {
meillo@177 177 DEBUG(4) debugf("S: %s\n", psb->buffer);
meillo@177 178 }
meillo@0 179
meillo@10 180 return TRUE;
meillo@0 181 }
meillo@0 182
meillo@10 183 static gboolean
meillo@10 184 check_response(smtp_base * psb, gboolean after_data)
meillo@0 185 {
meillo@10 186 char c = psb->buffer[0];
meillo@0 187
meillo@10 188 if (((c == '2') && !after_data) || ((c == '3') && after_data)) {
meillo@10 189 psb->error = smtp_ok;
meillo@176 190 DEBUG(6) debugf("response OK:'%s' after_data = %d\n", psb->buffer, (int) after_data);
meillo@10 191 return TRUE;
meillo@10 192 } else {
meillo@10 193 if (c == '4')
meillo@10 194 psb->error = smtp_trylater;
meillo@10 195 else if (c == '5')
meillo@10 196 psb->error = smtp_fail;
meillo@10 197 else
meillo@10 198 psb->error = smtp_syntax;
meillo@176 199 DEBUG(6) debugf("response failure:'%s' after_data = %d\n", psb->buffer, (int) after_data);
meillo@10 200 return FALSE;
meillo@10 201 }
meillo@0 202 }
meillo@0 203
meillo@10 204 static gboolean
meillo@10 205 check_init_response(smtp_base * psb)
meillo@0 206 {
meillo@10 207 if (check_response(psb, FALSE)) {
meillo@10 208 psb->use_esmtp = (strstr(psb->buffer, "ESMTP") != NULL);
meillo@0 209
meillo@10 210 DEBUG(4) debugf(psb->use_esmtp ? "uses esmtp\n" : "no esmtp\n");
meillo@0 211
meillo@10 212 return TRUE;
meillo@10 213 }
meillo@10 214 return FALSE;
meillo@0 215 }
meillo@0 216
meillo@10 217 static gchar*
meillo@10 218 get_response_arg(gchar * response)
meillo@0 219 {
meillo@10 220 gchar buf[SMTP_BUF_LEN];
meillo@10 221 gchar *p = response, *q = buf;
meillo@0 222
meillo@10 223 while (*p && (*p != '\n') && isspace(*p))
meillo@10 224 p++;
meillo@10 225 if (*p && (*p != '\n')) {
meillo@10 226 while (*p && (*p != '\n') && (*p != '\r') && (q < buf + SMTP_BUF_LEN - 1))
meillo@10 227 *(q++) = *(p++);
meillo@15 228 *q = '\0';
meillo@10 229 return g_strdup(buf);
meillo@10 230 }
meillo@10 231 return NULL;
meillo@0 232 }
meillo@0 233
meillo@10 234 static gboolean
meillo@10 235 check_helo_response(smtp_base * psb)
meillo@0 236 {
meillo@222 237 gchar *ptr;
meillo@0 238
meillo@10 239 if (!check_response(psb, FALSE))
meillo@10 240 return FALSE;
meillo@0 241
meillo@222 242 if (psb->last_code == 220) {
meillo@222 243 logwrite(LOG_NOTICE, "received a 220 greeting after sending EHLO,\n");
meillo@222 244 logwrite(LOG_NOTICE, "please remove `instant_helo' from your route config\n");
meillo@222 245 /* read the next response, cause that's the actual helo response */
meillo@222 246 if (!read_response(psb, SMTP_CMD_TIMEOUT) || !check_response(psb, FALSE)) {
meillo@222 247 return FALSE;
meillo@222 248 }
meillo@222 249 }
meillo@222 250
meillo@222 251 ptr = psb->buffer;
meillo@222 252
meillo@10 253 while (*ptr) {
meillo@10 254 if (strncasecmp(&(ptr[4]), "SIZE", 4) == 0) {
meillo@10 255 gchar *arg;
meillo@10 256 psb->use_size = TRUE;
meillo@10 257 arg = get_response_arg(&(ptr[8]));
meillo@10 258 if (arg) {
meillo@10 259 psb->max_size = atoi(arg);
meillo@10 260 g_free(arg);
meillo@10 261 }
meillo@10 262 }
meillo@0 263
meillo@10 264 if (strncasecmp(&(ptr[4]), "PIPELINING", 10) == 0)
meillo@10 265 psb->use_pipelining = TRUE;
meillo@0 266
meillo@10 267 if (strncasecmp(&(ptr[4]), "AUTH", 4) == 0) {
meillo@10 268 if ((ptr[8] == ' ') || (ptr[8] == '=') || (ptr[8] == '\t')) { /* not sure about '\t' */
meillo@10 269 gchar *arg;
meillo@10 270 psb->use_auth = TRUE;
meillo@10 271 arg = get_response_arg(&(ptr[9])); /* after several years I finally learnt to count */
meillo@10 272 if (arg) {
meillo@10 273 psb->auth_names = g_strsplit(arg, " ", 0);
meillo@10 274 g_free(arg);
meillo@10 275
meillo@10 276 DEBUG(4) {
meillo@10 277 gint i = 0;
meillo@114 278 debugf("in check_helo_response()\n");
meillo@10 279 while (psb->auth_names[i]) {
meillo@114 280 debugf(" offered AUTH %s\n", psb->auth_names[i]);
meillo@10 281 i++;
meillo@10 282 }
meillo@10 283 }
meillo@10 284 }
meillo@10 285 }
meillo@10 286 }
meillo@10 287
meillo@10 288 while (*ptr != '\n')
meillo@10 289 ptr++;
meillo@10 290 ptr++;
meillo@0 291 }
meillo@0 292
meillo@10 293 DEBUG(4) {
meillo@114 294 debugf(" %s\n", psb->use_size ? "uses SIZE" : "no size");
meillo@114 295 debugf(" %s\n", psb->use_pipelining ? "uses PIPELINING" : "no pipelining");
meillo@114 296 debugf(" %s\n", psb->use_auth ? "uses AUTH" : "no auth");
meillo@10 297 }
meillo@0 298
meillo@10 299 return TRUE;
meillo@0 300 }
meillo@0 301
meillo@10 302 static gboolean
meillo@10 303 smtp_helo(smtp_base * psb, gchar * helo)
meillo@0 304 {
meillo@10 305 while (TRUE) {
meillo@10 306 if (psb->use_esmtp) {
meillo@10 307 fprintf(psb->out, "EHLO %s\r\n", helo);
meillo@10 308 fflush(psb->out);
meillo@0 309
meillo@177 310 DEBUG(4) debugf("C: EHLO %s\r\n", helo);
meillo@0 311
meillo@10 312 } else {
meillo@10 313 fprintf(psb->out, "HELO %s\r\n", helo);
meillo@10 314 fflush(psb->out);
meillo@0 315
meillo@177 316 DEBUG(4) debugf("C: HELO %s\r\n", helo);
meillo@0 317
meillo@10 318 }
meillo@0 319
meillo@10 320 if (!read_response(psb, SMTP_CMD_TIMEOUT))
meillo@10 321 return FALSE;
meillo@10 322
meillo@10 323 if (check_helo_response(psb))
meillo@10 324 return TRUE;
meillo@10 325 else {
meillo@10 326 if (psb->error == smtp_fail) {
meillo@10 327 if (psb->use_esmtp) {
meillo@10 328 /* our guess that server understands EHLO was wrong, try again with HELO */
meillo@10 329 psb->use_esmtp = FALSE;
meillo@10 330 } else {
meillo@10 331 /* what sort of server ist THAT ?! give up... */
meillo@10 332 return FALSE;
meillo@10 333 }
meillo@10 334 } else
meillo@10 335 return FALSE;
meillo@10 336 }
meillo@0 337 }
meillo@0 338 }
meillo@0 339
meillo@10 340 static void
meillo@10 341 smtp_cmd_mailfrom(smtp_base * psb, address * return_path, guint size)
meillo@0 342 {
meillo@10 343 if (psb->use_size) {
meillo@10 344 fprintf(psb->out, "MAIL FROM:%s SIZE=%d\r\n", addr_string(return_path), size);
meillo@10 345 fflush(psb->out);
meillo@0 346
meillo@177 347 DEBUG(4) debugf("C: MAIL FROM:%s SIZE=%d\r\n", addr_string(return_path), size);
meillo@0 348
meillo@10 349 } else {
meillo@10 350 fprintf(psb->out, "MAIL FROM:%s\r\n", addr_string(return_path));
meillo@10 351 fflush(psb->out);
meillo@0 352
meillo@177 353 DEBUG(4) debugf("C: MAIL FROM:%s\r\n", addr_string(return_path));
meillo@10 354 }
meillo@0 355 }
meillo@0 356
meillo@10 357 static void
meillo@10 358 smtp_cmd_rcptto(smtp_base * psb, address * rcpt)
meillo@0 359 {
meillo@10 360 fprintf(psb->out, "RCPT TO:%s\r\n", addr_string(rcpt));
meillo@10 361 fflush(psb->out);
meillo@177 362 DEBUG(4) debugf("C: RCPT TO:%s\n", addr_string(rcpt));
meillo@0 363 }
meillo@0 364
meillo@10 365 static void
meillo@10 366 send_data_line(smtp_base * psb, gchar * data)
meillo@0 367 {
meillo@10 368 /* According to RFC 821 each line should be terminated with CRLF.
meillo@10 369 Since a dot on a line itself marks the end of data, each line
meillo@10 370 beginning with a dot is prepended with another dot.
meillo@10 371 */
meillo@10 372 gchar *ptr;
meillo@15 373 gboolean new_line = TRUE; /* previous versions assumed that each item was exactly one line.
meillo@15 374 This is no longer the case */
meillo@0 375
meillo@10 376 ptr = data;
meillo@10 377 while (*ptr) {
meillo@10 378 int c = (int) (*ptr);
meillo@119 379 if (c == '.' && new_line) {
meillo@119 380 /* dot-stuffing */
meillo@119 381 putc('.', psb->out);
meillo@119 382 }
meillo@10 383 if (c == '\n') {
meillo@119 384 /* CRLF line terminators */
meillo@10 385 putc('\r', psb->out);
meillo@10 386 putc('\n', psb->out);
meillo@10 387 new_line = TRUE;
meillo@10 388 } else {
meillo@10 389 putc(c, psb->out);
meillo@10 390 new_line = FALSE;
meillo@10 391 }
meillo@10 392 ptr++;
meillo@10 393 }
meillo@0 394 }
meillo@0 395
meillo@10 396 static void
meillo@10 397 send_header(smtp_base * psb, GList * hdr_list)
meillo@0 398 {
meillo@10 399 GList *node;
meillo@10 400 gint num_hdrs = 0;
meillo@0 401
meillo@10 402 /* header */
meillo@10 403 if (hdr_list) {
meillo@10 404 foreach(hdr_list, node) {
meillo@10 405 if (node->data) {
meillo@10 406 header *hdr = (header *) (node->data);
meillo@10 407 if (hdr->header) {
meillo@10 408 send_data_line(psb, hdr->header);
meillo@10 409 num_hdrs++;
meillo@10 410 }
meillo@10 411 }
meillo@10 412 }
meillo@0 413 }
meillo@0 414
meillo@10 415 /* empty line separating headers from data: */
meillo@10 416 putc('\r', psb->out);
meillo@10 417 putc('\n', psb->out);
meillo@0 418
meillo@10 419 DEBUG(4) debugf("sent %d headers\n", num_hdrs);
meillo@0 420 }
meillo@0 421
meillo@10 422 static void
meillo@10 423 send_data(smtp_base * psb, message * msg)
meillo@0 424 {
meillo@10 425 GList *node;
meillo@10 426 gint num_lines = 0;
meillo@0 427
meillo@10 428 /* data */
meillo@10 429 if (msg->data_list) {
meillo@10 430 for (node = g_list_first(msg->data_list); node; node = g_list_next(node)) {
meillo@10 431 if (node->data) {
meillo@10 432 send_data_line(psb, node->data);
meillo@10 433 num_lines++;
meillo@10 434 }
meillo@10 435 }
meillo@10 436 }
meillo@0 437
meillo@10 438 DEBUG(4) debugf("sent %d lines of data\n", num_lines);
meillo@0 439
meillo@10 440 fprintf(psb->out, ".\r\n");
meillo@10 441 fflush(psb->out);
meillo@177 442 DEBUG(4) debugf("C: .\n");
meillo@0 443 }
meillo@0 444
meillo@10 445 void
meillo@10 446 smtp_out_mark_rcpts(smtp_base * psb, GList * rcpt_list)
meillo@0 447 {
meillo@10 448 GList *rcpt_node;
meillo@10 449 for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 450 address *rcpt = (address *) (rcpt_node->data);
meillo@0 451
meillo@10 452 addr_unmark_delivered(rcpt);
meillo@0 453
meillo@10 454 if ((psb->error == smtp_trylater) || (psb->error == smtp_timeout) || (psb->error == smtp_eof)) {
meillo@10 455 addr_mark_defered(rcpt);
meillo@10 456 } else {
meillo@10 457 addr_mark_failed(rcpt);
meillo@10 458 }
meillo@10 459 }
meillo@0 460 }
meillo@0 461
meillo@10 462 void
meillo@10 463 smtp_out_log_failure(smtp_base * psb, message * msg)
meillo@0 464 {
meillo@10 465 gchar *err_str;
meillo@0 466
meillo@10 467 if (psb->error == smtp_timeout)
meillo@10 468 err_str = g_strdup("connection timed out.");
meillo@10 469 else if (psb->error == smtp_eof)
meillo@10 470 err_str = g_strdup("connection terminated prematurely.");
meillo@10 471 else if (psb->error == smtp_syntax)
meillo@10 472 err_str = g_strdup_printf("got unexpected response: %s", psb->buffer);
meillo@10 473 else if (psb->error == smtp_cancel)
meillo@10 474 err_str = g_strdup("delivery was canceled.\n");
meillo@10 475 else
meillo@10 476 /* error message should still be in the buffer */
meillo@10 477 err_str = g_strdup_printf("failed: %s\n", psb->buffer);
meillo@0 478
meillo@10 479 if (msg == NULL)
meillo@10 480 logwrite(LOG_NOTICE, "host=%s %s\n", psb->remote_host, err_str);
meillo@10 481 else
meillo@10 482 logwrite(LOG_NOTICE, "%s == host=%s %s\n", msg->uid, psb->remote_host, err_str);
meillo@0 483
meillo@10 484 g_free(err_str);
meillo@0 485 }
meillo@0 486
meillo@10 487 smtp_base*
meillo@10 488 smtp_out_open(gchar * host, gint port, GList * resolve_list)
meillo@0 489 {
meillo@10 490 smtp_base *psb;
meillo@10 491 gint sock;
meillo@10 492 mxip_addr *addr;
meillo@0 493
meillo@10 494 DEBUG(5) debugf("smtp_out_open entered, host = %s\n", host);
meillo@0 495
meillo@10 496 if ((addr = connect_resolvelist(&sock, host, port, resolve_list))) {
meillo@10 497 /* create structure to hold status data: */
meillo@10 498 psb = create_smtpbase(sock);
meillo@10 499 psb->remote_host = addr->name;
meillo@0 500
meillo@10 501 DEBUG(5) {
meillo@10 502 struct sockaddr_in name;
meillo@10 503 int len = sizeof(struct sockaddr);
meillo@10 504 getsockname(sock, (struct sockaddr *) (&name), &len);
meillo@10 505 debugf("socket: name.sin_addr = %s\n", inet_ntoa(name.sin_addr));
meillo@10 506 }
meillo@10 507 return psb;
meillo@10 508 } else {
meillo@10 509 DEBUG(5) debugf("connect_resolvelist failed: %s %s\n", strerror(errno), hstrerror(h_errno));
meillo@10 510 }
meillo@0 511
meillo@10 512 return NULL;
meillo@0 513 }
meillo@0 514
meillo@10 515 smtp_base*
meillo@10 516 smtp_out_open_child(gchar * cmd)
meillo@0 517 {
meillo@10 518 smtp_base *psb;
meillo@10 519 gint sock;
meillo@0 520
meillo@10 521 DEBUG(5) debugf("smtp_out_open_child entered, cmd = %s\n", cmd);
meillo@0 522
meillo@10 523 sock = child(cmd);
meillo@0 524
meillo@10 525 if (sock > 0) {
meillo@10 526 psb = create_smtpbase(sock);
meillo@10 527 psb->remote_host = NULL;
meillo@0 528
meillo@10 529 return psb;
meillo@10 530 }
meillo@0 531
meillo@10 532 return NULL;
meillo@0 533 }
meillo@0 534
meillo@10 535 gboolean
meillo@10 536 smtp_out_rset(smtp_base * psb)
meillo@0 537 {
meillo@10 538 gboolean ok;
meillo@0 539
meillo@10 540 fprintf(psb->out, "RSET\r\n");
meillo@10 541 fflush(psb->out);
meillo@177 542 DEBUG(4) debugf("C: RSET\n");
meillo@0 543
meillo@10 544 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 545 if (check_response(psb, FALSE))
meillo@10 546 return TRUE;
meillo@0 547
meillo@10 548 smtp_out_log_failure(psb, NULL);
meillo@10 549
meillo@10 550 return FALSE;
meillo@0 551 }
meillo@0 552
meillo@0 553 #ifdef ENABLE_AUTH
meillo@0 554
meillo@10 555 static gboolean
meillo@10 556 smtp_out_auth_cram_md5(smtp_base * psb)
meillo@0 557 {
meillo@10 558 gboolean ok = FALSE;
meillo@0 559
meillo@177 560 fprintf(psb->out, "C: AUTH CRAM-MD5\r\n");
meillo@10 561 fflush(psb->out);
meillo@10 562 DEBUG(4) debugf("AUTH CRAM-MD5\n");
meillo@10 563 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 564 if ((ok = check_response(psb, TRUE))) {
meillo@10 565 gchar *chall64 = get_response_arg(&(psb->buffer[4]));
meillo@10 566 gint chall_size;
meillo@10 567 gchar *chall = base64_decode(chall64, &chall_size);
meillo@10 568 guchar digest[16], *reply64, *reply;
meillo@10 569 gchar digest_string[33];
meillo@10 570 gint i;
meillo@0 571 #ifdef USE_LIB_CRYPTO
meillo@10 572 unsigned int digest_len;
meillo@0 573 #endif
meillo@10 574
meillo@114 575 DEBUG(5) debugf("smtp_out_auth_cram_md5():\n");
meillo@114 576 DEBUG(5) debugf(" encoded challenge = %s\n", chall64);
meillo@114 577 DEBUG(5) debugf(" decoded challenge = %s, size = %d\n", chall, chall_size);
meillo@114 578 DEBUG(5) debugf(" secret = %s\n", psb->auth_secret);
meillo@10 579
meillo@0 580 #ifdef USE_LIB_CRYPTO
meillo@10 581 HMAC(EVP_md5(), psb->auth_secret, strlen(psb->auth_secret), chall, chall_size, digest, &digest_len);
meillo@0 582 #else
meillo@10 583 hmac_md5(chall, chall_size, psb->auth_secret, strlen(psb->auth_secret), digest);
meillo@0 584 #endif
meillo@10 585
meillo@10 586 for (i = 0; i < 16; i++)
meillo@10 587 sprintf(&(digest_string[i + i]), "%02x", (unsigned int) (digest[i]));
meillo@15 588 digest_string[32] = '\0';
meillo@10 589
meillo@114 590 DEBUG(5) debugf(" digest = %s\n", digest_string);
meillo@10 591
meillo@10 592 reply = g_strdup_printf("%s %s", psb->auth_login, digest_string);
meillo@114 593 DEBUG(5) debugf(" unencoded reply = %s\n", reply);
meillo@10 594
meillo@10 595 reply64 = base64_encode(reply, strlen(reply));
meillo@114 596 DEBUG(5) debugf(" encoded reply = %s\n", reply64);
meillo@10 597
meillo@10 598 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 599 fflush(psb->out);
meillo@177 600 DEBUG(6) debugf(" reply64 = %s\n", reply64);
meillo@177 601 DEBUG(6) debugf("C: %s\n", reply64);
meillo@10 602
meillo@10 603 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 604 ok = check_response(psb, FALSE);
meillo@10 605
meillo@10 606 g_free(reply64);
meillo@10 607 g_free(reply);
meillo@10 608 g_free(chall);
meillo@10 609 g_free(chall64);
meillo@10 610 }
meillo@10 611 }
meillo@10 612 return ok;
meillo@0 613 }
meillo@0 614
meillo@10 615 static gboolean
meillo@10 616 smtp_out_auth_login(smtp_base * psb)
meillo@0 617 {
meillo@10 618 gboolean ok = FALSE;
meillo@10 619 fprintf(psb->out, "AUTH LOGIN\r\n");
meillo@10 620 fflush(psb->out);
meillo@177 621 DEBUG(4) debugf("C: AUTH LOGIN\r\n");
meillo@10 622 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 623 if ((ok = check_response(psb, TRUE))) {
meillo@10 624 gchar *resp64;
meillo@10 625 guchar *resp;
meillo@10 626 gint resp_size;
meillo@10 627 gchar *reply64;
meillo@10 628
meillo@114 629 DEBUG(5) debugf("smtp_out_auth_login():\n");
meillo@10 630 resp64 = get_response_arg(&(psb->buffer[4]));
meillo@114 631 DEBUG(5) debugf(" encoded response = %s\n", resp64);
meillo@10 632 resp = base64_decode(resp64, &resp_size);
meillo@10 633 g_free(resp64);
meillo@114 634 DEBUG(5) debugf(" decoded response = %s, size = %d\n", resp, resp_size);
meillo@10 635 g_free(resp);
meillo@10 636 reply64 = base64_encode(psb->auth_login, strlen(psb->auth_login));
meillo@10 637 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 638 fflush(psb->out);
meillo@177 639 DEBUG(6) debugf("C: %s\n", reply64);
meillo@10 640 g_free(reply64);
meillo@10 641 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 642 if ((ok = check_response(psb, TRUE))) {
meillo@10 643 resp64 = get_response_arg(&(psb->buffer[4]));
meillo@114 644 DEBUG(5) debugf(" encoded response = %s\n", resp64);
meillo@10 645 resp = base64_decode(resp64, &resp_size);
meillo@10 646 g_free(resp64);
meillo@114 647 DEBUG(5) debugf(" decoded response = %s, size = %d\n", resp, resp_size);
meillo@10 648 g_free(resp);
meillo@10 649 reply64 = base64_encode(psb->auth_secret, strlen(psb->auth_secret));
meillo@10 650 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 651 fflush(psb->out);
meillo@177 652 DEBUG(6) debugf("C: %s\n", reply64);
meillo@10 653 g_free(reply64);
meillo@10 654 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 655 ok = check_response(psb, FALSE);
meillo@10 656 }
meillo@10 657 }
meillo@10 658 }
meillo@0 659 }
meillo@10 660 return ok;
meillo@0 661 }
meillo@0 662
meillo@10 663 gboolean
meillo@10 664 smtp_out_auth(smtp_base * psb)
meillo@0 665 {
meillo@10 666 gboolean ok = FALSE;
meillo@10 667 gint i = 0;
meillo@10 668 while (psb->auth_names[i]) {
meillo@10 669 if (strcasecmp(psb->auth_names[i], psb->auth_name) == 0)
meillo@10 670 break;
meillo@10 671 i++;
meillo@10 672 }
meillo@10 673 if (psb->auth_names[i]) {
meillo@10 674 if (strcasecmp(psb->auth_name, "cram-md5") == 0) {
meillo@10 675 smtp_out_auth_cram_md5(psb);
meillo@10 676 } else if (strcasecmp(psb->auth_name, "login") == 0) {
meillo@10 677 smtp_out_auth_login(psb);
meillo@10 678 } else {
meillo@10 679 logwrite(LOG_ERR, "auth method %s not supported\n", psb->auth_name);
meillo@10 680 }
meillo@10 681 } else {
meillo@10 682 logwrite(LOG_ERR, "no auth method %s found.\n", psb->auth_name);
meillo@10 683 }
meillo@10 684 return ok;
meillo@0 685 }
meillo@0 686
meillo@0 687 #endif
meillo@0 688
meillo@10 689 gboolean
meillo@222 690 smtp_out_init(smtp_base * psb, gboolean instant_helo)
meillo@0 691 {
meillo@10 692 gboolean ok;
meillo@0 693
meillo@222 694 logwrite(LOG_INFO, "smtp_out_init(): instant_helo:%d\n", instant_helo);
meillo@10 695
meillo@222 696 if (instant_helo) {
meillo@222 697 /* we say hello right away, hence we don't know if
meillo@222 698 ESMTP is supported; we just assume it */
meillo@222 699 psb->use_esmtp = 1;
meillo@222 700 } else {
meillo@222 701 if ((ok = read_response(psb, SMTP_INITIAL_TIMEOUT))) {
meillo@222 702 ok = check_init_response(psb);
meillo@222 703 }
meillo@222 704 if (!ok) {
meillo@222 705 smtp_out_log_failure(psb, NULL);
meillo@222 706 return ok;
meillo@222 707 }
meillo@222 708 }
meillo@222 709
meillo@222 710 if ((ok = smtp_helo(psb, psb->helo_name))) {
meillo@0 711 #ifdef ENABLE_AUTH
meillo@222 712 if (psb->auth_name && psb->use_auth) {
meillo@222 713 /* we completely disregard the response of server here. If
meillo@222 714 authentication fails, the server will complain later
meillo@222 715 anyway. I know, this is not polite... */
meillo@222 716 smtp_out_auth(psb);
meillo@222 717 }
meillo@10 718 #endif
meillo@0 719 }
meillo@10 720 if (!ok)
meillo@10 721 smtp_out_log_failure(psb, NULL);
meillo@10 722 return ok;
meillo@0 723 }
meillo@0 724
meillo@10 725 gint
meillo@10 726 smtp_out_msg(smtp_base * psb, message * msg, address * return_path, GList * rcpt_list, GList * hdr_list)
meillo@0 727 {
meillo@10 728 gint i, size;
meillo@10 729 gboolean ok = TRUE;
meillo@10 730 int rcpt_cnt;
meillo@10 731 int rcpt_accept = 0;
meillo@0 732
meillo@10 733 DEBUG(5) debugf("smtp_out_msg entered\n");
meillo@0 734
meillo@10 735 /* defaults: */
meillo@10 736 if (return_path == NULL)
meillo@10 737 return_path = msg->return_path;
meillo@10 738 if (hdr_list == NULL)
meillo@10 739 hdr_list = msg->hdr_list;
meillo@10 740 if (rcpt_list == NULL)
meillo@10 741 rcpt_list = msg->rcpt_list;
meillo@10 742 rcpt_cnt = g_list_length(rcpt_list);
meillo@0 743
meillo@10 744 size = msg_calc_size(msg, TRUE);
meillo@0 745
meillo@10 746 /* respect maximum size given by server: */
meillo@10 747 if ((psb->max_size > 0) && (size > psb->max_size)) {
meillo@114 748 logwrite(LOG_WARNING, "%s == host=%s message size (%d) > "
meillo@114 749 "fixed maximum message size of server (%d)",
meillo@10 750 msg->uid, psb->remote_host, size, psb->max_size);
meillo@10 751 psb->error = smtp_cancel;
meillo@10 752 ok = FALSE;
meillo@10 753 }
meillo@0 754
meillo@10 755 if (ok) {
meillo@119 756 /* pretend the message is a bit larger,
meillo@119 757 just in case the size calculation is buggy */
meillo@119 758 smtp_cmd_mailfrom(psb, return_path, psb->use_size ? size+SMTP_SIZE_ADD : 0);
meillo@0 759
meillo@10 760 if (!psb->use_pipelining) {
meillo@10 761 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 762 ok = check_response(psb, FALSE);
meillo@10 763 }
meillo@10 764 }
meillo@10 765 if (ok) {
meillo@10 766 GList *rcpt_node;
meillo@10 767 rcpt_accept = 0;
meillo@0 768
meillo@10 769 for (rcpt_node = g_list_first(rcpt_list); rcpt_node != NULL; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 770 address *rcpt = (address *) (rcpt_node->data);
meillo@10 771 smtp_cmd_rcptto(psb, rcpt);
meillo@10 772 if (!psb->use_pipelining) {
meillo@10 773 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 774 if (check_response(psb, FALSE)) {
meillo@10 775 rcpt_accept++;
meillo@10 776 addr_mark_delivered(rcpt);
meillo@10 777 } else {
meillo@10 778 /* if server returned an error for one recp. we
meillo@10 779 may still try the others. But if it is a timeout, eof
meillo@10 780 or unexpected response, it is more serious and we should
meillo@10 781 give up. */
meillo@10 782 if ((psb->error != smtp_trylater) && (psb->error != smtp_fail)) {
meillo@10 783 ok = FALSE;
meillo@10 784 break;
meillo@10 785 } else {
meillo@114 786 logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s\n",
meillo@15 787 msg->uid, addr_string(rcpt), psb->remote_host, psb->buffer);
meillo@10 788 if (psb->error == smtp_trylater) {
meillo@10 789 addr_mark_defered(rcpt);
meillo@10 790 } else {
meillo@10 791 addr_mark_failed(rcpt);
meillo@10 792 }
meillo@10 793 }
meillo@10 794 } else
meillo@10 795 break;
meillo@10 796 }
meillo@10 797 }
meillo@0 798
meillo@10 799 /* There is no point in going on if no recp.s were accpted.
meillo@10 800 But we can check that at this point only if not pipelining: */
meillo@10 801 ok = (ok && (psb->use_pipelining || (rcpt_accept > 0)));
meillo@10 802 if (ok) {
meillo@0 803
meillo@10 804 fprintf(psb->out, "DATA\r\n");
meillo@10 805 fflush(psb->out);
meillo@0 806
meillo@177 807 DEBUG(4) debugf("C: DATA\r\n");
meillo@10 808
meillo@10 809 if (psb->use_pipelining) {
meillo@10 810 /* the first pl'ed command was MAIL FROM
meillo@10 811 the last was DATA, whose response can be handled by the 'normal' code
meillo@10 812 all in between were RCPT TO:
meillo@10 813 */
meillo@10 814 /* response to MAIL FROM: */
meillo@10 815 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 816 if ((ok = check_response(psb, FALSE))) {
meillo@10 817
meillo@10 818 /* response(s) to RCPT TO:
meillo@10 819 this is very similar to the sequence above for no pipeline
meillo@10 820 */
meillo@10 821 for (i = 0; i < rcpt_cnt; i++) {
meillo@10 822 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 823 address *rcpt = g_list_nth_data(rcpt_list, i);
meillo@10 824 if (check_response(psb, FALSE)) {
meillo@10 825 rcpt_accept++;
meillo@10 826 addr_mark_delivered(rcpt);
meillo@10 827 } else {
meillo@10 828 /* if server returned an error 4xx or 5xx for one recp. we
meillo@10 829 may still try the others. But if it is a timeout, eof
meillo@10 830 or unexpected response, it is more serious and we
meillo@10 831 should give up. */
meillo@10 832 if ((psb->error != smtp_trylater) &&
meillo@10 833 (psb->error != smtp_fail)) {
meillo@10 834 ok = FALSE;
meillo@10 835 break;
meillo@10 836 } else {
meillo@114 837 logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s\n", msg->uid,
meillo@10 838 addr_string(rcpt), psb->remote_host, psb->buffer);
meillo@10 839 if (psb->error == smtp_trylater) {
meillo@10 840 addr_mark_defered(rcpt);
meillo@10 841 } else {
meillo@10 842 addr_mark_failed(rcpt);
meillo@10 843 }
meillo@10 844 }
meillo@10 845 }
meillo@10 846 } else {
meillo@10 847 DEBUG(5) debugf("check_response failed after RCPT TO\n");
meillo@10 848 break;
meillo@10 849 }
meillo@10 850 }
meillo@10 851 if (rcpt_accept == 0)
meillo@10 852 ok = FALSE;
meillo@10 853 } else {
meillo@10 854 DEBUG(5) debugf("check_response failed after MAIL FROM\n");
meillo@10 855 }
meillo@10 856 } else {
meillo@10 857 DEBUG(5)
meillo@10 858 debugf("read_response failed after MAIL FROM\n");
meillo@10 859 }
meillo@10 860 }
meillo@10 861
meillo@10 862 /* if(psb->use_pipelining) */
meillo@10 863 /* response to the DATA cmd */
meillo@10 864 if (ok) {
meillo@10 865 if (read_response(psb, SMTP_DATA_TIMEOUT)) {
meillo@10 866 if (check_response(psb, TRUE)) {
meillo@10 867 send_header(psb, hdr_list);
meillo@10 868 send_data(psb, msg);
meillo@10 869
meillo@10 870 if (read_response(psb, SMTP_FINAL_TIMEOUT))
meillo@10 871 ok = check_response(psb, FALSE);
meillo@10 872 }
meillo@10 873 }
meillo@10 874 }
meillo@0 875 }
meillo@10 876 }
meillo@10 877
meillo@10 878 DEBUG(5) {
meillo@114 879 debugf("smtp_out_msg():\n");
meillo@114 880 debugf(" psb->error = %d\n", psb->error);
meillo@114 881 debugf(" ok = %d\n", ok);
meillo@114 882 debugf(" rcpt_accept = %d\n", rcpt_accept);
meillo@10 883 }
meillo@10 884
meillo@10 885 if (psb->error == smtp_ok) {
meillo@10 886 GList *rcpt_node;
meillo@10 887 for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 888 address *rcpt = (address *) (rcpt_node->data);
meillo@10 889 if (addr_is_delivered(rcpt))
meillo@10 890 logwrite(LOG_NOTICE, "%s => %s host=%s with %s\n", msg->uid, addr_string(rcpt),
meillo@10 891 psb->remote_host, psb->use_esmtp ? "esmtp" : "smtp");
meillo@0 892 }
meillo@10 893 } else {
meillo@10 894 /* if something went wrong,
meillo@10 895 we have to unmark the rcpts prematurely marked as delivered
meillo@10 896 and mark the status */
meillo@10 897 smtp_out_mark_rcpts(psb, rcpt_list);
meillo@10 898
meillo@10 899 /* log the failure: */
meillo@10 900 smtp_out_log_failure(psb, msg);
meillo@0 901 }
meillo@10 902 return rcpt_accept;
meillo@0 903 }
meillo@0 904
meillo@10 905 gboolean
meillo@10 906 smtp_out_quit(smtp_base * psb)
meillo@0 907 {
meillo@10 908 fprintf(psb->out, "QUIT\r\n");
meillo@10 909 fflush(psb->out);
meillo@0 910
meillo@177 911 DEBUG(4) debugf("C: QUIT\n");
meillo@0 912
meillo@10 913 signal(SIGALRM, SIG_DFL);
meillo@10 914
meillo@10 915 return TRUE;
meillo@0 916 }
meillo@10 917
meillo@10 918 gint
meillo@10 919 smtp_deliver(gchar * host, gint port, GList * resolve_list, message * msg, address * return_path, GList * rcpt_list)
meillo@0 920 {
meillo@10 921 smtp_base *psb;
meillo@10 922 smtp_error err;
meillo@0 923
meillo@10 924 DEBUG(5) debugf("smtp_deliver entered\n");
meillo@0 925
meillo@10 926 if (return_path == NULL)
meillo@10 927 return_path = msg->return_path;
meillo@0 928
meillo@10 929 if ((psb = smtp_out_open(host, port, resolve_list))) {
meillo@10 930 set_heloname(psb, return_path->domain, TRUE);
meillo@10 931 /* initiate connection, send message and quit: */
meillo@222 932 if (smtp_out_init(psb, FALSE)) {
meillo@10 933 smtp_out_msg(psb, msg, return_path, rcpt_list, NULL);
meillo@10 934 if (psb->error == smtp_ok || (psb->error == smtp_fail) || (psb->error == smtp_trylater)
meillo@10 935 || (psb->error == smtp_syntax) || (psb->error == smtp_cancel))
meillo@10 936 smtp_out_quit(psb);
meillo@10 937 }
meillo@10 938
meillo@10 939 err = psb->error;
meillo@10 940 destroy_smtpbase(psb);
meillo@10 941
meillo@10 942 return err;
meillo@10 943 }
meillo@10 944 return -1;
meillo@0 945 }