masqmail

annotate src/smtp_out.c @ 323:29de6a1c4538

Fixed an important bug with folded headers! g_strconcat() returns a *copy* of the string, but hdr->value still pointed to the old header (which probably was a memory leak, too). If the folded part had been quite small it was likely that the new string was at the same position as the old one, thus making everything go well. But if pretty long headers were folded several times it was likely that the new string was allocated somewhere else in memory, thus breaking things. In result mails to lots of recipients (folded header) were frequently only sent to the ones in the first line. Sorry for the inconvenience.
author meillo@marmaro.de
date Fri, 03 Jun 2011 09:47:27 +0200
parents c74adb7c4f50
children 02bc0331e390
rev   line source
meillo@224 1 /* smtp_out.c
meillo@224 2 Copyright (C) 1999-2001 Oliver Kurth
meillo@224 3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
meillo@224 4
meillo@0 5 * This program is free software; you can redistribute it and/or modify
meillo@0 6 * it under the terms of the GNU General Public License as published by
meillo@0 7 * the Free Software Foundation; either version 2 of the License, or
meillo@0 8 * (at your option) any later version.
meillo@10 9 *
meillo@0 10 * This program is distributed in the hope that it will be useful,
meillo@0 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@0 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@0 13 * GNU General Public License for more details.
meillo@0 14 *
meillo@0 15 * You should have received a copy of the GNU General Public License
meillo@0 16 * along with this program; if not, write to the Free Software
meillo@0 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
meillo@0 18 */
meillo@0 19
meillo@0 20 /*
meillo@0 21 I always forget these rfc numbers:
meillo@0 22 RFC 821 (SMTP)
meillo@0 23 RFC 1869 (ESMTP)
meillo@0 24 RFC 1870 (ESMTP SIZE)
meillo@0 25 RFC 2197 (ESMTP PIPELINE)
meillo@0 26 RFC 2554 (ESMTP AUTH)
meillo@0 27 */
meillo@0 28
meillo@0 29 #include "masqmail.h"
meillo@0 30 #include "smtp_out.h"
meillo@0 31 #include "readsock.h"
meillo@0 32
meillo@0 33 #ifdef ENABLE_AUTH
meillo@0 34
meillo@0 35 #ifdef USE_LIB_CRYPTO
meillo@0 36 #include <openssl/hmac.h>
meillo@0 37 #include <openssl/md5.h>
meillo@0 38 #include <openssl/evp.h>
meillo@0 39 #else
meillo@0 40 #include "md5/md5.h"
meillo@0 41 #include "md5/hmac_md5.h"
meillo@0 42 #endif
meillo@0 43
meillo@0 44 #include "base64/base64.h"
meillo@0 45 #endif
meillo@0 46
meillo@10 47 void
meillo@10 48 destroy_smtpbase(smtp_base * psb)
meillo@0 49 {
meillo@10 50 fclose(psb->in);
meillo@10 51 fclose(psb->out);
meillo@0 52
meillo@10 53 close(psb->sock);
meillo@0 54
meillo@10 55 if (psb->helo_name)
meillo@10 56 g_free(psb->helo_name);
meillo@10 57 if (psb->buffer)
meillo@10 58 g_free(psb->buffer);
meillo@10 59 if (psb->auth_names)
meillo@10 60 g_strfreev(psb->auth_names);
meillo@0 61
meillo@10 62 if (psb->auth_name)
meillo@10 63 g_free(psb->auth_name);
meillo@10 64 if (psb->auth_login)
meillo@10 65 g_free(psb->auth_login);
meillo@10 66 if (psb->auth_secret)
meillo@10 67 g_free(psb->auth_secret);
meillo@0 68 }
meillo@0 69
meillo@10 70 gchar*
meillo@10 71 set_heloname(smtp_base * psb, gchar * default_name, gboolean do_correct)
meillo@0 72 {
meillo@10 73 struct sockaddr_in sname;
meillo@10 74 int len = sizeof(struct sockaddr_in);
meillo@10 75 struct hostent *host_entry;
meillo@0 76
meillo@10 77 if (do_correct) {
meillo@10 78 getsockname(psb->sock, (struct sockaddr *) (&sname), &len);
meillo@10 79 DEBUG(5) debugf("socket: name.sin_addr = %s\n", inet_ntoa(sname.sin_addr));
meillo@10 80 host_entry = gethostbyaddr((const char *) &(sname.sin_addr), sizeof(sname.sin_addr), AF_INET);
meillo@10 81 if (host_entry) {
meillo@10 82 psb->helo_name = g_strdup(host_entry->h_name);
meillo@10 83 } else {
meillo@10 84 /* we failed to look up our own name. Instead of giving our local hostname,
meillo@10 85 we may give our IP number to show the server that we are at least
meillo@10 86 willing to be honest. For the really picky ones. */
meillo@10 87 DEBUG(5) debugf("failed to look up own host name.\n");
meillo@10 88 psb->helo_name = g_strdup_printf("[%s]", inet_ntoa(sname.sin_addr));
meillo@10 89 }
meillo@10 90 DEBUG(5) debugf("helo_name = %s\n", psb->helo_name);
meillo@10 91 }
meillo@10 92 if (psb->helo_name == NULL) {
meillo@10 93 psb->helo_name = g_strdup(default_name);
meillo@10 94 }
meillo@10 95 return psb->helo_name;
meillo@10 96 }
meillo@0 97
meillo@0 98 #ifdef ENABLE_AUTH
meillo@0 99
meillo@10 100 gboolean
meillo@10 101 set_auth(smtp_base * psb, gchar * name, gchar * login, gchar * secret)
meillo@0 102 {
meillo@10 103 if ((strcasecmp(name, "CRAM-MD5") == 0) || (strcasecmp(name, "LOGIN") == 0)) {
meillo@10 104 psb->auth_name = g_strdup(name);
meillo@10 105 psb->auth_login = g_strdup(login);
meillo@10 106 psb->auth_secret = g_strdup(secret);
meillo@10 107
meillo@10 108 return TRUE;
meillo@10 109 }
meillo@10 110 return FALSE;
meillo@0 111 }
meillo@0 112
meillo@0 113 #endif
meillo@0 114
meillo@10 115 static smtp_base*
meillo@10 116 create_smtpbase(gint sock)
meillo@0 117 {
meillo@10 118 gint dup_sock;
meillo@0 119
meillo@10 120 smtp_base *psb = (smtp_base *) g_malloc(sizeof(smtp_base));
meillo@0 121
meillo@10 122 psb->sock = sock;
meillo@0 123
meillo@10 124 psb->use_size = FALSE;
meillo@10 125 psb->use_pipelining = FALSE;
meillo@10 126 psb->use_auth = FALSE;
meillo@0 127
meillo@10 128 psb->max_size = 0;
meillo@10 129 psb->auth_names = NULL;
meillo@0 130
meillo@10 131 psb->buffer = (gchar *) g_malloc(SMTP_BUF_LEN);
meillo@0 132
meillo@10 133 dup_sock = dup(sock);
meillo@10 134 psb->out = fdopen(sock, "w");
meillo@10 135 psb->in = fdopen(dup_sock, "r");
meillo@0 136
meillo@10 137 psb->error = smtp_ok;
meillo@0 138
meillo@10 139 psb->helo_name = NULL;
meillo@0 140
meillo@10 141 psb->auth_name = psb->auth_login = psb->auth_secret = NULL;
meillo@10 142
meillo@10 143 return psb;
meillo@0 144 }
meillo@0 145
meillo@10 146 static gboolean
meillo@10 147 read_response(smtp_base * psb, int timeout)
meillo@0 148 {
meillo@10 149 gint buf_pos = 0;
meillo@10 150 gchar code[5];
meillo@10 151 gint i, len;
meillo@0 152
meillo@10 153 do {
meillo@10 154 len = read_sockline(psb->in, &(psb->buffer[buf_pos]), SMTP_BUF_LEN - buf_pos, timeout, READSOCKL_CHUG);
meillo@10 155 if (len == -3) {
meillo@10 156 psb->error = smtp_timeout;
meillo@10 157 return FALSE;
meillo@10 158 } else if (len == -2) {
meillo@10 159 psb->error = smtp_syntax;
meillo@10 160 return FALSE;
meillo@10 161 } else if (len == -1) {
meillo@10 162 psb->error = smtp_eof;
meillo@10 163 return FALSE;
meillo@10 164 }
meillo@10 165 for (i = 0; i < 4; i++)
meillo@10 166 code[i] = psb->buffer[buf_pos + i];
meillo@15 167 code[i] = '\0';
meillo@10 168 psb->last_code = atoi(code);
meillo@0 169
meillo@10 170 buf_pos += len;
meillo@0 171
meillo@10 172 } while (code[3] == '-');
meillo@177 173 if (psb->buffer) {
meillo@177 174 DEBUG(4) debugf("S: %s\n", psb->buffer);
meillo@177 175 }
meillo@0 176
meillo@10 177 return TRUE;
meillo@0 178 }
meillo@0 179
meillo@10 180 static gboolean
meillo@10 181 check_response(smtp_base * psb, gboolean after_data)
meillo@0 182 {
meillo@10 183 char c = psb->buffer[0];
meillo@0 184
meillo@10 185 if (((c == '2') && !after_data) || ((c == '3') && after_data)) {
meillo@10 186 psb->error = smtp_ok;
meillo@176 187 DEBUG(6) debugf("response OK:'%s' after_data = %d\n", psb->buffer, (int) after_data);
meillo@10 188 return TRUE;
meillo@10 189 } else {
meillo@10 190 if (c == '4')
meillo@10 191 psb->error = smtp_trylater;
meillo@10 192 else if (c == '5')
meillo@10 193 psb->error = smtp_fail;
meillo@10 194 else
meillo@10 195 psb->error = smtp_syntax;
meillo@176 196 DEBUG(6) debugf("response failure:'%s' after_data = %d\n", psb->buffer, (int) after_data);
meillo@10 197 return FALSE;
meillo@10 198 }
meillo@0 199 }
meillo@0 200
meillo@10 201 static gchar*
meillo@10 202 get_response_arg(gchar * response)
meillo@0 203 {
meillo@10 204 gchar buf[SMTP_BUF_LEN];
meillo@10 205 gchar *p = response, *q = buf;
meillo@0 206
meillo@10 207 while (*p && (*p != '\n') && isspace(*p))
meillo@10 208 p++;
meillo@10 209 if (*p && (*p != '\n')) {
meillo@10 210 while (*p && (*p != '\n') && (*p != '\r') && (q < buf + SMTP_BUF_LEN - 1))
meillo@10 211 *(q++) = *(p++);
meillo@15 212 *q = '\0';
meillo@10 213 return g_strdup(buf);
meillo@10 214 }
meillo@10 215 return NULL;
meillo@0 216 }
meillo@0 217
meillo@10 218 static gboolean
meillo@10 219 check_helo_response(smtp_base * psb)
meillo@0 220 {
meillo@222 221 gchar *ptr;
meillo@0 222
meillo@10 223 if (!check_response(psb, FALSE))
meillo@10 224 return FALSE;
meillo@0 225
meillo@222 226 if (psb->last_code == 220) {
meillo@222 227 logwrite(LOG_NOTICE, "received a 220 greeting after sending EHLO,\n");
meillo@222 228 logwrite(LOG_NOTICE, "please remove `instant_helo' from your route config\n");
meillo@222 229 /* read the next response, cause that's the actual helo response */
meillo@222 230 if (!read_response(psb, SMTP_CMD_TIMEOUT) || !check_response(psb, FALSE)) {
meillo@222 231 return FALSE;
meillo@222 232 }
meillo@222 233 }
meillo@222 234
meillo@222 235 ptr = psb->buffer;
meillo@222 236
meillo@10 237 while (*ptr) {
meillo@10 238 if (strncasecmp(&(ptr[4]), "SIZE", 4) == 0) {
meillo@10 239 gchar *arg;
meillo@10 240 psb->use_size = TRUE;
meillo@10 241 arg = get_response_arg(&(ptr[8]));
meillo@10 242 if (arg) {
meillo@10 243 psb->max_size = atoi(arg);
meillo@10 244 g_free(arg);
meillo@10 245 }
meillo@10 246 }
meillo@0 247
meillo@10 248 if (strncasecmp(&(ptr[4]), "PIPELINING", 10) == 0)
meillo@10 249 psb->use_pipelining = TRUE;
meillo@0 250
meillo@10 251 if (strncasecmp(&(ptr[4]), "AUTH", 4) == 0) {
meillo@10 252 if ((ptr[8] == ' ') || (ptr[8] == '=') || (ptr[8] == '\t')) { /* not sure about '\t' */
meillo@10 253 gchar *arg;
meillo@10 254 psb->use_auth = TRUE;
meillo@10 255 arg = get_response_arg(&(ptr[9])); /* after several years I finally learnt to count */
meillo@10 256 if (arg) {
meillo@10 257 psb->auth_names = g_strsplit(arg, " ", 0);
meillo@10 258 g_free(arg);
meillo@10 259
meillo@10 260 DEBUG(4) {
meillo@10 261 gint i = 0;
meillo@114 262 debugf("in check_helo_response()\n");
meillo@10 263 while (psb->auth_names[i]) {
meillo@114 264 debugf(" offered AUTH %s\n", psb->auth_names[i]);
meillo@10 265 i++;
meillo@10 266 }
meillo@10 267 }
meillo@10 268 }
meillo@10 269 }
meillo@10 270 }
meillo@10 271
meillo@10 272 while (*ptr != '\n')
meillo@10 273 ptr++;
meillo@10 274 ptr++;
meillo@0 275 }
meillo@0 276
meillo@10 277 DEBUG(4) {
meillo@114 278 debugf(" %s\n", psb->use_size ? "uses SIZE" : "no size");
meillo@114 279 debugf(" %s\n", psb->use_pipelining ? "uses PIPELINING" : "no pipelining");
meillo@114 280 debugf(" %s\n", psb->use_auth ? "uses AUTH" : "no auth");
meillo@10 281 }
meillo@0 282
meillo@10 283 return TRUE;
meillo@0 284 }
meillo@0 285
meillo@246 286 /*
meillo@246 287 We first try EHLO, but if it fails HELO in a second fall back try.
meillo@246 288 This is what is requested by RFC 2821 (sec 3.2):
meillo@246 289
meillo@246 290 Once the server has sent the welcoming message and
meillo@246 291 the client has received it, the client normally sends
meillo@246 292 the EHLO command to the server, [...]
meillo@246 293 For a particular connection attempt, if the server
meillo@246 294 returns a "command not recognized" response to EHLO,
meillo@246 295 the client SHOULD be able to fall back and send HELO.
meillo@246 296
meillo@246 297 Up to and including version 0.3.0 masqmail used ESMTP only if the
meillo@246 298 string ``ESMTP'' appeared within the server's greeting message. This
meillo@246 299 made it impossible to use AUTH with servers that would send odd
meillo@246 300 greeting messages.
meillo@246 301 */
meillo@10 302 static gboolean
meillo@10 303 smtp_helo(smtp_base * psb, gchar * helo)
meillo@0 304 {
meillo@246 305 fprintf(psb->out, "EHLO %s\r\n", helo);
meillo@246 306 fflush(psb->out);
meillo@246 307 DEBUG(4) debugf("C: EHLO %s\r\n", helo);
meillo@0 308
meillo@246 309 if (!read_response(psb, SMTP_CMD_TIMEOUT)) {
meillo@246 310 return FALSE;
meillo@246 311 }
meillo@246 312 if (check_helo_response(psb)) {
meillo@246 313 DEBUG(4) debugf("uses esmtp\n");
meillo@246 314 return TRUE;
meillo@246 315 }
meillo@0 316
meillo@246 317 if (psb->error != smtp_fail) {
meillo@246 318 return FALSE;
meillo@246 319 }
meillo@0 320
meillo@246 321 /* our guess that server understands EHLO could have been wrong,
meillo@246 322 try again with HELO */
meillo@0 323
meillo@246 324 fprintf(psb->out, "HELO %s\r\n", helo);
meillo@246 325 fflush(psb->out);
meillo@246 326 DEBUG(4) debugf("C: HELO %s\r\n", helo);
meillo@0 327
meillo@246 328 if (!read_response(psb, SMTP_CMD_TIMEOUT)) {
meillo@246 329 return FALSE;
meillo@246 330 }
meillo@246 331 if (check_helo_response(psb)) {
meillo@246 332 DEBUG(4) debugf("uses smtp\n");
meillo@246 333 return TRUE;
meillo@246 334 }
meillo@10 335
meillo@246 336 /* what sort of server ist THAT ?! give up... */
meillo@246 337 return FALSE;
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@321 516 smtp_out_open_child(gchar * cmd, char* host)
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@321 522 psb->remote_host = host;
meillo@10 523 sock = child(cmd);
meillo@10 524 if (sock > 0) {
meillo@10 525 psb = create_smtpbase(sock);
meillo@10 526 psb->remote_host = NULL;
meillo@0 527
meillo@10 528 return psb;
meillo@10 529 }
meillo@0 530
meillo@10 531 return NULL;
meillo@0 532 }
meillo@0 533
meillo@10 534 gboolean
meillo@10 535 smtp_out_rset(smtp_base * psb)
meillo@0 536 {
meillo@10 537 gboolean ok;
meillo@0 538
meillo@10 539 fprintf(psb->out, "RSET\r\n");
meillo@10 540 fflush(psb->out);
meillo@177 541 DEBUG(4) debugf("C: RSET\n");
meillo@0 542
meillo@10 543 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 544 if (check_response(psb, FALSE))
meillo@10 545 return TRUE;
meillo@0 546
meillo@10 547 smtp_out_log_failure(psb, NULL);
meillo@10 548
meillo@10 549 return FALSE;
meillo@0 550 }
meillo@0 551
meillo@0 552 #ifdef ENABLE_AUTH
meillo@0 553
meillo@10 554 static gboolean
meillo@10 555 smtp_out_auth_cram_md5(smtp_base * psb)
meillo@0 556 {
meillo@10 557 gboolean ok = FALSE;
meillo@0 558
meillo@177 559 fprintf(psb->out, "C: AUTH CRAM-MD5\r\n");
meillo@10 560 fflush(psb->out);
meillo@10 561 DEBUG(4) debugf("AUTH CRAM-MD5\n");
meillo@10 562 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 563 if ((ok = check_response(psb, TRUE))) {
meillo@10 564 gchar *chall64 = get_response_arg(&(psb->buffer[4]));
meillo@10 565 gint chall_size;
meillo@10 566 gchar *chall = base64_decode(chall64, &chall_size);
meillo@10 567 guchar digest[16], *reply64, *reply;
meillo@10 568 gchar digest_string[33];
meillo@10 569 gint i;
meillo@0 570 #ifdef USE_LIB_CRYPTO
meillo@10 571 unsigned int digest_len;
meillo@0 572 #endif
meillo@10 573
meillo@114 574 DEBUG(5) debugf("smtp_out_auth_cram_md5():\n");
meillo@114 575 DEBUG(5) debugf(" encoded challenge = %s\n", chall64);
meillo@114 576 DEBUG(5) debugf(" decoded challenge = %s, size = %d\n", chall, chall_size);
meillo@114 577 DEBUG(5) debugf(" secret = %s\n", psb->auth_secret);
meillo@10 578
meillo@0 579 #ifdef USE_LIB_CRYPTO
meillo@10 580 HMAC(EVP_md5(), psb->auth_secret, strlen(psb->auth_secret), chall, chall_size, digest, &digest_len);
meillo@0 581 #else
meillo@10 582 hmac_md5(chall, chall_size, psb->auth_secret, strlen(psb->auth_secret), digest);
meillo@0 583 #endif
meillo@10 584
meillo@10 585 for (i = 0; i < 16; i++)
meillo@10 586 sprintf(&(digest_string[i + i]), "%02x", (unsigned int) (digest[i]));
meillo@15 587 digest_string[32] = '\0';
meillo@10 588
meillo@114 589 DEBUG(5) debugf(" digest = %s\n", digest_string);
meillo@10 590
meillo@10 591 reply = g_strdup_printf("%s %s", psb->auth_login, digest_string);
meillo@114 592 DEBUG(5) debugf(" unencoded reply = %s\n", reply);
meillo@10 593
meillo@10 594 reply64 = base64_encode(reply, strlen(reply));
meillo@114 595 DEBUG(5) debugf(" encoded reply = %s\n", reply64);
meillo@10 596
meillo@10 597 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 598 fflush(psb->out);
meillo@177 599 DEBUG(6) debugf(" reply64 = %s\n", reply64);
meillo@177 600 DEBUG(6) debugf("C: %s\n", reply64);
meillo@10 601
meillo@10 602 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 603 ok = check_response(psb, FALSE);
meillo@10 604
meillo@10 605 g_free(reply64);
meillo@10 606 g_free(reply);
meillo@10 607 g_free(chall);
meillo@10 608 g_free(chall64);
meillo@10 609 }
meillo@10 610 }
meillo@10 611 return ok;
meillo@0 612 }
meillo@0 613
meillo@10 614 static gboolean
meillo@10 615 smtp_out_auth_login(smtp_base * psb)
meillo@0 616 {
meillo@10 617 gboolean ok = FALSE;
meillo@10 618 fprintf(psb->out, "AUTH LOGIN\r\n");
meillo@10 619 fflush(psb->out);
meillo@177 620 DEBUG(4) debugf("C: AUTH LOGIN\r\n");
meillo@10 621 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 622 if ((ok = check_response(psb, TRUE))) {
meillo@10 623 gchar *resp64;
meillo@10 624 guchar *resp;
meillo@10 625 gint resp_size;
meillo@10 626 gchar *reply64;
meillo@10 627
meillo@114 628 DEBUG(5) debugf("smtp_out_auth_login():\n");
meillo@10 629 resp64 = get_response_arg(&(psb->buffer[4]));
meillo@312 630 DEBUG(5) debugf(" encoded response = `%s'\n", resp64);
meillo@10 631 resp = base64_decode(resp64, &resp_size);
meillo@10 632 g_free(resp64);
meillo@312 633 DEBUG(5) debugf(" decoded response = `%s', size = %d\n", resp, resp_size);
meillo@10 634 g_free(resp);
meillo@10 635 reply64 = base64_encode(psb->auth_login, strlen(psb->auth_login));
meillo@10 636 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 637 fflush(psb->out);
meillo@177 638 DEBUG(6) debugf("C: %s\n", reply64);
meillo@10 639 g_free(reply64);
meillo@10 640 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 641 if ((ok = check_response(psb, TRUE))) {
meillo@10 642 resp64 = get_response_arg(&(psb->buffer[4]));
meillo@312 643 DEBUG(5) debugf(" encoded response = `%s'\n", resp64);
meillo@10 644 resp = base64_decode(resp64, &resp_size);
meillo@10 645 g_free(resp64);
meillo@312 646 DEBUG(5) debugf(" decoded response = `%s', size = %d\n", resp, resp_size);
meillo@10 647 g_free(resp);
meillo@10 648 reply64 = base64_encode(psb->auth_secret, strlen(psb->auth_secret));
meillo@10 649 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 650 fflush(psb->out);
meillo@177 651 DEBUG(6) debugf("C: %s\n", reply64);
meillo@10 652 g_free(reply64);
meillo@10 653 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 654 ok = check_response(psb, FALSE);
meillo@10 655 }
meillo@10 656 }
meillo@10 657 }
meillo@0 658 }
meillo@10 659 return ok;
meillo@0 660 }
meillo@0 661
meillo@10 662 gboolean
meillo@10 663 smtp_out_auth(smtp_base * psb)
meillo@0 664 {
meillo@10 665 gboolean ok = FALSE;
meillo@10 666 gint i = 0;
meillo@10 667 while (psb->auth_names[i]) {
meillo@10 668 if (strcasecmp(psb->auth_names[i], psb->auth_name) == 0)
meillo@10 669 break;
meillo@10 670 i++;
meillo@10 671 }
meillo@10 672 if (psb->auth_names[i]) {
meillo@10 673 if (strcasecmp(psb->auth_name, "cram-md5") == 0) {
meillo@10 674 smtp_out_auth_cram_md5(psb);
meillo@10 675 } else if (strcasecmp(psb->auth_name, "login") == 0) {
meillo@10 676 smtp_out_auth_login(psb);
meillo@10 677 } else {
meillo@10 678 logwrite(LOG_ERR, "auth method %s not supported\n", psb->auth_name);
meillo@10 679 }
meillo@10 680 } else {
meillo@10 681 logwrite(LOG_ERR, "no auth method %s found.\n", psb->auth_name);
meillo@10 682 }
meillo@10 683 return ok;
meillo@0 684 }
meillo@0 685
meillo@0 686 #endif
meillo@0 687
meillo@10 688 gboolean
meillo@222 689 smtp_out_init(smtp_base * psb, gboolean instant_helo)
meillo@0 690 {
meillo@10 691 gboolean ok;
meillo@0 692
meillo@222 693 logwrite(LOG_INFO, "smtp_out_init(): instant_helo:%d\n", instant_helo);
meillo@10 694
meillo@246 695 if (!instant_helo) {
meillo@222 696 if ((ok = read_response(psb, SMTP_INITIAL_TIMEOUT))) {
meillo@246 697 ok = check_response(psb, FALSE);
meillo@222 698 }
meillo@222 699 if (!ok) {
meillo@222 700 smtp_out_log_failure(psb, NULL);
meillo@222 701 return ok;
meillo@222 702 }
meillo@222 703 }
meillo@222 704
meillo@222 705 if ((ok = smtp_helo(psb, psb->helo_name))) {
meillo@0 706 #ifdef ENABLE_AUTH
meillo@222 707 if (psb->auth_name && psb->use_auth) {
meillo@222 708 /* we completely disregard the response of server here. If
meillo@222 709 authentication fails, the server will complain later
meillo@222 710 anyway. I know, this is not polite... */
meillo@222 711 smtp_out_auth(psb);
meillo@222 712 }
meillo@10 713 #endif
meillo@0 714 }
meillo@10 715 if (!ok)
meillo@10 716 smtp_out_log_failure(psb, NULL);
meillo@10 717 return ok;
meillo@0 718 }
meillo@0 719
meillo@10 720 gint
meillo@10 721 smtp_out_msg(smtp_base * psb, message * msg, address * return_path, GList * rcpt_list, GList * hdr_list)
meillo@0 722 {
meillo@10 723 gint i, size;
meillo@10 724 gboolean ok = TRUE;
meillo@10 725 int rcpt_cnt;
meillo@10 726 int rcpt_accept = 0;
meillo@0 727
meillo@10 728 DEBUG(5) debugf("smtp_out_msg entered\n");
meillo@0 729
meillo@10 730 /* defaults: */
meillo@10 731 if (return_path == NULL)
meillo@10 732 return_path = msg->return_path;
meillo@10 733 if (hdr_list == NULL)
meillo@10 734 hdr_list = msg->hdr_list;
meillo@10 735 if (rcpt_list == NULL)
meillo@10 736 rcpt_list = msg->rcpt_list;
meillo@10 737 rcpt_cnt = g_list_length(rcpt_list);
meillo@0 738
meillo@10 739 size = msg_calc_size(msg, TRUE);
meillo@0 740
meillo@10 741 /* respect maximum size given by server: */
meillo@10 742 if ((psb->max_size > 0) && (size > psb->max_size)) {
meillo@114 743 logwrite(LOG_WARNING, "%s == host=%s message size (%d) > "
meillo@114 744 "fixed maximum message size of server (%d)",
meillo@10 745 msg->uid, psb->remote_host, size, psb->max_size);
meillo@10 746 psb->error = smtp_cancel;
meillo@10 747 ok = FALSE;
meillo@10 748 }
meillo@0 749
meillo@10 750 if (ok) {
meillo@119 751 /* pretend the message is a bit larger,
meillo@119 752 just in case the size calculation is buggy */
meillo@119 753 smtp_cmd_mailfrom(psb, return_path, psb->use_size ? size+SMTP_SIZE_ADD : 0);
meillo@0 754
meillo@10 755 if (!psb->use_pipelining) {
meillo@10 756 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 757 ok = check_response(psb, FALSE);
meillo@10 758 }
meillo@10 759 }
meillo@10 760 if (ok) {
meillo@10 761 GList *rcpt_node;
meillo@10 762 rcpt_accept = 0;
meillo@0 763
meillo@10 764 for (rcpt_node = g_list_first(rcpt_list); rcpt_node != NULL; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 765 address *rcpt = (address *) (rcpt_node->data);
meillo@10 766 smtp_cmd_rcptto(psb, rcpt);
meillo@10 767 if (!psb->use_pipelining) {
meillo@10 768 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 769 if (check_response(psb, FALSE)) {
meillo@10 770 rcpt_accept++;
meillo@10 771 addr_mark_delivered(rcpt);
meillo@10 772 } else {
meillo@10 773 /* if server returned an error for one recp. we
meillo@10 774 may still try the others. But if it is a timeout, eof
meillo@10 775 or unexpected response, it is more serious and we should
meillo@10 776 give up. */
meillo@10 777 if ((psb->error != smtp_trylater) && (psb->error != smtp_fail)) {
meillo@10 778 ok = FALSE;
meillo@10 779 break;
meillo@10 780 } else {
meillo@114 781 logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s\n",
meillo@15 782 msg->uid, addr_string(rcpt), psb->remote_host, psb->buffer);
meillo@10 783 if (psb->error == smtp_trylater) {
meillo@10 784 addr_mark_defered(rcpt);
meillo@10 785 } else {
meillo@10 786 addr_mark_failed(rcpt);
meillo@10 787 }
meillo@10 788 }
meillo@10 789 } else
meillo@10 790 break;
meillo@10 791 }
meillo@10 792 }
meillo@0 793
meillo@10 794 /* There is no point in going on if no recp.s were accpted.
meillo@10 795 But we can check that at this point only if not pipelining: */
meillo@10 796 ok = (ok && (psb->use_pipelining || (rcpt_accept > 0)));
meillo@10 797 if (ok) {
meillo@0 798
meillo@10 799 fprintf(psb->out, "DATA\r\n");
meillo@10 800 fflush(psb->out);
meillo@0 801
meillo@177 802 DEBUG(4) debugf("C: DATA\r\n");
meillo@10 803
meillo@10 804 if (psb->use_pipelining) {
meillo@10 805 /* the first pl'ed command was MAIL FROM
meillo@10 806 the last was DATA, whose response can be handled by the 'normal' code
meillo@10 807 all in between were RCPT TO:
meillo@10 808 */
meillo@10 809 /* response to MAIL FROM: */
meillo@10 810 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 811 if ((ok = check_response(psb, FALSE))) {
meillo@10 812
meillo@10 813 /* response(s) to RCPT TO:
meillo@10 814 this is very similar to the sequence above for no pipeline
meillo@10 815 */
meillo@10 816 for (i = 0; i < rcpt_cnt; i++) {
meillo@10 817 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 818 address *rcpt = g_list_nth_data(rcpt_list, i);
meillo@10 819 if (check_response(psb, FALSE)) {
meillo@10 820 rcpt_accept++;
meillo@10 821 addr_mark_delivered(rcpt);
meillo@10 822 } else {
meillo@10 823 /* if server returned an error 4xx or 5xx for one recp. we
meillo@10 824 may still try the others. But if it is a timeout, eof
meillo@10 825 or unexpected response, it is more serious and we
meillo@10 826 should give up. */
meillo@10 827 if ((psb->error != smtp_trylater) &&
meillo@10 828 (psb->error != smtp_fail)) {
meillo@10 829 ok = FALSE;
meillo@10 830 break;
meillo@10 831 } else {
meillo@114 832 logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s\n", msg->uid,
meillo@10 833 addr_string(rcpt), psb->remote_host, psb->buffer);
meillo@10 834 if (psb->error == smtp_trylater) {
meillo@10 835 addr_mark_defered(rcpt);
meillo@10 836 } else {
meillo@10 837 addr_mark_failed(rcpt);
meillo@10 838 }
meillo@10 839 }
meillo@10 840 }
meillo@10 841 } else {
meillo@10 842 DEBUG(5) debugf("check_response failed after RCPT TO\n");
meillo@10 843 break;
meillo@10 844 }
meillo@10 845 }
meillo@10 846 if (rcpt_accept == 0)
meillo@10 847 ok = FALSE;
meillo@10 848 } else {
meillo@10 849 DEBUG(5) debugf("check_response failed after MAIL FROM\n");
meillo@10 850 }
meillo@10 851 } else {
meillo@10 852 DEBUG(5)
meillo@10 853 debugf("read_response failed after MAIL FROM\n");
meillo@10 854 }
meillo@10 855 }
meillo@10 856
meillo@10 857 /* if(psb->use_pipelining) */
meillo@10 858 /* response to the DATA cmd */
meillo@10 859 if (ok) {
meillo@10 860 if (read_response(psb, SMTP_DATA_TIMEOUT)) {
meillo@10 861 if (check_response(psb, TRUE)) {
meillo@10 862 send_header(psb, hdr_list);
meillo@10 863 send_data(psb, msg);
meillo@10 864
meillo@10 865 if (read_response(psb, SMTP_FINAL_TIMEOUT))
meillo@10 866 ok = check_response(psb, FALSE);
meillo@10 867 }
meillo@10 868 }
meillo@10 869 }
meillo@0 870 }
meillo@10 871 }
meillo@10 872
meillo@10 873 DEBUG(5) {
meillo@114 874 debugf("smtp_out_msg():\n");
meillo@114 875 debugf(" psb->error = %d\n", psb->error);
meillo@114 876 debugf(" ok = %d\n", ok);
meillo@114 877 debugf(" rcpt_accept = %d\n", rcpt_accept);
meillo@10 878 }
meillo@10 879
meillo@10 880 if (psb->error == smtp_ok) {
meillo@10 881 GList *rcpt_node;
meillo@10 882 for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 883 address *rcpt = (address *) (rcpt_node->data);
meillo@10 884 if (addr_is_delivered(rcpt))
meillo@246 885 logwrite(LOG_NOTICE, "%s => %s host=%s\n",
meillo@246 886 msg->uid, addr_string(rcpt), psb->remote_host);
meillo@0 887 }
meillo@10 888 } else {
meillo@10 889 /* if something went wrong,
meillo@10 890 we have to unmark the rcpts prematurely marked as delivered
meillo@10 891 and mark the status */
meillo@10 892 smtp_out_mark_rcpts(psb, rcpt_list);
meillo@10 893
meillo@10 894 /* log the failure: */
meillo@10 895 smtp_out_log_failure(psb, msg);
meillo@0 896 }
meillo@10 897 return rcpt_accept;
meillo@0 898 }
meillo@0 899
meillo@10 900 gboolean
meillo@10 901 smtp_out_quit(smtp_base * psb)
meillo@0 902 {
meillo@10 903 fprintf(psb->out, "QUIT\r\n");
meillo@10 904 fflush(psb->out);
meillo@0 905
meillo@177 906 DEBUG(4) debugf("C: QUIT\n");
meillo@0 907
meillo@10 908 signal(SIGALRM, SIG_DFL);
meillo@10 909
meillo@10 910 return TRUE;
meillo@0 911 }
meillo@10 912
meillo@10 913 gint
meillo@10 914 smtp_deliver(gchar * host, gint port, GList * resolve_list, message * msg, address * return_path, GList * rcpt_list)
meillo@0 915 {
meillo@10 916 smtp_base *psb;
meillo@10 917 smtp_error err;
meillo@0 918
meillo@10 919 DEBUG(5) debugf("smtp_deliver entered\n");
meillo@0 920
meillo@10 921 if (return_path == NULL)
meillo@10 922 return_path = msg->return_path;
meillo@0 923
meillo@10 924 if ((psb = smtp_out_open(host, port, resolve_list))) {
meillo@10 925 set_heloname(psb, return_path->domain, TRUE);
meillo@10 926 /* initiate connection, send message and quit: */
meillo@222 927 if (smtp_out_init(psb, FALSE)) {
meillo@10 928 smtp_out_msg(psb, msg, return_path, rcpt_list, NULL);
meillo@10 929 if (psb->error == smtp_ok || (psb->error == smtp_fail) || (psb->error == smtp_trylater)
meillo@10 930 || (psb->error == smtp_syntax) || (psb->error == smtp_cancel))
meillo@10 931 smtp_out_quit(psb);
meillo@10 932 }
meillo@10 933
meillo@10 934 err = psb->error;
meillo@10 935 destroy_smtpbase(psb);
meillo@10 936
meillo@10 937 return err;
meillo@10 938 }
meillo@10 939 return -1;
meillo@0 940 }