masqmail

annotate src/smtp_in.c @ 209:10da50168dab

replaced the MD5 implementation with the one of Solar Designer Until now, the sample code of RFC 1321 was used. It had an ugly license. Now we use the implementation of Solar Designer, which is in the Public Domain. http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
author meillo@marmaro.de
date Sun, 18 Jul 2010 21:58:15 +0200
parents 5745edd5b769
children 794071925a22
rev   line source
meillo@0 1 /* MasqMail
meillo@0 2 Copyright (C) 1999-2001 Oliver Kurth
meillo@80 3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
meillo@0 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@0 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
meillo@0 18 */
meillo@0 19
meillo@0 20 #include "masqmail.h"
meillo@0 21 #include "readsock.h"
meillo@0 22
meillo@0 23 /*
meillo@0 24 I always forget these rfc numbers:
meillo@0 25 RFC 821 (SMTP)
meillo@0 26 RFC 1869 (ESMTP)
meillo@0 27 RFC 1870 (ESMTP SIZE)
meillo@0 28 RFC 2197 (ESMTP PIPELINE)
meillo@0 29 RFC 2554 (ESMTP AUTH)
meillo@0 30 */
meillo@0 31
meillo@0 32
meillo@10 33 smtp_cmd smtp_cmds[] = {
meillo@15 34 {SMTP_HELO, "HELO",},
meillo@15 35 {SMTP_EHLO, "EHLO",},
meillo@15 36 {SMTP_MAIL_FROM, "MAIL FROM:",},
meillo@15 37 {SMTP_RCPT_TO, "RCPT TO:",},
meillo@15 38 {SMTP_DATA, "DATA",},
meillo@15 39 {SMTP_QUIT, "QUIT",},
meillo@15 40 {SMTP_RSET, "RSET",},
meillo@15 41 {SMTP_NOOP, "NOOP",},
meillo@15 42 {SMTP_HELP, "HELP"},
meillo@0 43 };
meillo@0 44
meillo@10 45 static smtp_cmd_id
meillo@10 46 get_id(const gchar * line)
meillo@0 47 {
meillo@10 48 gint i;
meillo@10 49 for (i = 0; i < SMTP_NUM_IDS; i++) {
meillo@80 50 if (strncasecmp(smtp_cmds[i].cmd, line, strlen(smtp_cmds[i].cmd)) == 0) {
meillo@10 51 return (smtp_cmd_id) i;
meillo@80 52 }
meillo@10 53 }
meillo@10 54 return SMTP_ERROR;
meillo@0 55 }
meillo@0 56
meillo@117 57 static gboolean
meillo@117 58 get_size(gchar *line, unsigned long *msize) {
meillo@117 59 gchar *s = NULL;
meillo@117 60
meillo@117 61 /* hope we need not to handle cases like SiZe= ...*/
meillo@117 62 s = strstr(line, "SIZE=");
meillo@117 63 if (!s) {
meillo@117 64 /* try it in lowercase too */
meillo@117 65 if (!(s = strstr(line, "size="))) {
meillo@117 66 return FALSE;
meillo@117 67 }
meillo@117 68 }
meillo@117 69 s += 5;
meillo@117 70 *msize = atol(s);
meillo@117 71 DEBUG(5) debugf("get_size(): line=%s, msize=%ld\n", line, *msize);
meillo@117 72
meillo@117 73 return TRUE;
meillo@117 74 }
meillo@117 75
meillo@117 76
meillo@0 77 /* this is a quick hack: we expect the address to be syntactically correct
meillo@117 78 and containing the mailbox only, though we first check for size in
meillo@117 79 smtp_in().
meillo@136 80 Return false if address is too long.
meillo@0 81 */
meillo@10 82 static gboolean
meillo@10 83 get_address(gchar * line, gchar * addr)
meillo@0 84 {
meillo@80 85 gchar *p = line;
meillo@80 86 gchar *q = addr;
meillo@0 87
meillo@10 88 /* skip MAIL FROM: and RCPT TO: */
meillo@80 89 while (*p && (*p != ':')) {
meillo@10 90 p++;
meillo@80 91 }
meillo@10 92 p++;
meillo@0 93
meillo@10 94 /* skip spaces: */
meillo@80 95 while (*p && isspace(*p)) {
meillo@10 96 p++;
meillo@80 97 }
meillo@0 98
meillo@10 99 /* get address: */
meillo@136 100 while (*p && !isspace(*p)) {
meillo@136 101 if (q >= addr + MAX_ADDRESS-1) {
meillo@136 102 *q = '\0';
meillo@136 103 return FALSE;
meillo@136 104 }
meillo@10 105 *(q++) = *(p++);
meillo@80 106 }
meillo@136 107 *q = '\0';
meillo@0 108
meillo@10 109 return TRUE;
meillo@0 110 }
meillo@0 111
meillo@10 112 static smtp_connection*
meillo@10 113 create_base(gchar * remote_host)
meillo@0 114 {
meillo@10 115 smtp_connection *base = g_malloc(sizeof(smtp_connection));
meillo@80 116 if (!base) {
meillo@80 117 return NULL;
meillo@80 118 }
meillo@0 119
meillo@80 120 base->remote_host = g_strdup(remote_host);
meillo@0 121
meillo@80 122 base->prot = PROT_SMTP;
meillo@80 123 base->next_id = 0;
meillo@80 124 base->helo_seen = 0;
meillo@80 125 base->from_seen = 0;
meillo@80 126 base->rcpt_seen = 0;
meillo@80 127 base->msg = NULL;
meillo@80 128
meillo@80 129 return base;
meillo@0 130 }
meillo@0 131
meillo@10 132 static void
meillo@10 133 smtp_printf(FILE * out, gchar * fmt, ...)
meillo@0 134 {
meillo@10 135 va_list args;
meillo@10 136 va_start(args, fmt);
meillo@0 137
meillo@10 138 DEBUG(4) {
meillo@10 139 gchar buf[256];
meillo@10 140 va_list args_copy;
meillo@0 141
meillo@10 142 va_copy(args_copy, args);
meillo@10 143 vsnprintf(buf, 255, fmt, args_copy);
meillo@10 144 va_end(args_copy);
meillo@0 145
meillo@208 146 debugf(">>>%s\n", buf);
meillo@10 147 }
meillo@0 148
meillo@10 149 vfprintf(out, fmt, args);
meillo@10 150 fflush(out);
meillo@0 151
meillo@10 152 va_end(args);
meillo@0 153 }
meillo@0 154
meillo@10 155 void
meillo@10 156 smtp_in(FILE * in, FILE * out, gchar * remote_host, gchar * ident)
meillo@0 157 {
meillo@10 158 gchar *buffer;
meillo@10 159 smtp_cmd_id cmd_id;
meillo@10 160 message *msg = NULL;
meillo@10 161 smtp_connection *psc;
meillo@10 162 int len;
meillo@117 163 unsigned long size, msize;
meillo@0 164
meillo@10 165 DEBUG(5) debugf("smtp_in entered, remote_host = %s\n", remote_host);
meillo@0 166
meillo@10 167 psc = create_base(remote_host);
meillo@10 168 psc->msg = msg;
meillo@0 169
meillo@10 170 buffer = (gchar *) g_malloc(BUF_LEN);
meillo@80 171 if (!buffer) {
meillo@80 172 /* this check is actually unneccessary as g_malloc()
meillo@80 173 aborts on failure */
meillo@80 174 return;
meillo@80 175 }
meillo@0 176
meillo@80 177 /* send greeting string, containing ESMTP: */
meillo@80 178 smtp_printf(out, "220 %s MasqMail %s ESMTP\r\n", conf.host_name, VERSION);
meillo@10 179
meillo@80 180 while ((len = read_sockline(in, buffer, BUF_LEN, 5 * 60, READSOCKL_CHUG)) >= 0) {
meillo@80 181 cmd_id = get_id(buffer);
meillo@10 182
meillo@135 183 if (conf.defer_all) {
meillo@135 184 /* I need this to debug delivery failures */
meillo@127 185 smtp_printf(out, "421 %s service temporarily unavailable.\r\n", conf.host_name);
meillo@135 186 destroy_message(msg);
meillo@135 187 msg = NULL;
meillo@135 188 return;
meillo@127 189 }
meillo@127 190
meillo@80 191 switch (cmd_id) {
meillo@127 192 case SMTP_HELO:
meillo@127 193 psc->prot = PROT_SMTP;
meillo@127 194 psc->helo_seen = TRUE;
meillo@127 195 smtp_printf(out, "250 %s pretty old mailer, huh?\r\n", conf.host_name);
meillo@127 196 break;
meillo@127 197
meillo@80 198 case SMTP_EHLO:
meillo@80 199 psc->prot = PROT_ESMTP;
meillo@80 200 psc->helo_seen = TRUE;
meillo@127 201 smtp_printf(out, "250-%s Nice to meet you with ESMTP\r\n", conf.host_name);
meillo@127 202 smtp_printf(out, "250-SIZE %d\r\n", conf.max_msg_size);
meillo@127 203 smtp_printf(out, "250-PIPELINING\r\n");
meillo@127 204 smtp_printf(out, "250 HELP\r\n");
meillo@10 205 break;
meillo@80 206
meillo@80 207 case SMTP_MAIL_FROM:
meillo@80 208 {
meillo@80 209 gchar buf[MAX_ADDRESS];
meillo@80 210 address *addr;
meillo@80 211
meillo@80 212 if (!psc->helo_seen) {
meillo@80 213 smtp_printf(out, "503 need HELO or EHLO\r\n");
meillo@80 214 break;
meillo@80 215 }
meillo@80 216 if (psc->from_seen) {
meillo@80 217 smtp_printf(out, "503 MAIL FROM: already given.\r\n");
meillo@80 218 break;
meillo@80 219 }
meillo@128 220 if (get_size(buffer, &msize)) {
meillo@128 221 DEBUG(5) debugf("smtp_in(): get_size: msize=%ld, conf.mms=%d\n",
meillo@128 222 msize, conf.max_msg_size);
meillo@128 223 if (conf.max_msg_size && (msize > conf.max_msg_size)) {
meillo@128 224 smtp_printf(out, "552 Message size exceeds fixed limit.\r\n");
meillo@128 225 break;
meillo@128 226 }
meillo@128 227 }
meillo@136 228 if (!get_address(buffer, buf)) {
meillo@136 229 smtp_printf(out, "553 Address too long.\r\n");
meillo@136 230 break;
meillo@136 231 }
meillo@128 232
meillo@80 233 msg = create_message();
meillo@80 234 msg->received_host = remote_host ? g_strdup(remote_host) : NULL;
meillo@80 235 msg->received_prot = psc->prot;
meillo@80 236 msg->ident = ident ? g_strdup(ident) : NULL;
meillo@80 237 /* get transfer id and increment for next one */
meillo@80 238 msg->transfer_id = (psc->next_id)++;
meillo@80 239
meillo@80 240 if (remote_host) {
meillo@80 241 addr = create_address(buf, TRUE);
meillo@80 242 } else {
meillo@80 243 addr = create_address_qualified(buf, TRUE, conf.host_name);
meillo@80 244 }
meillo@80 245 if (!addr) {
meillo@80 246 smtp_printf(out, "501 %s: syntax error.\r\n", buf);
meillo@80 247 } else if (!addr->domain) {
meillo@80 248 smtp_printf(out, "501 return path must be qualified.\r\n", buf);
meillo@80 249 } else {
meillo@80 250 psc->from_seen = TRUE;
meillo@80 251 msg->return_path = addr;
meillo@80 252 smtp_printf(out, "250 OK %s is a nice guy.\r\n", addr->address);
meillo@80 253 }
meillo@80 254 }
meillo@10 255 break;
meillo@80 256
meillo@80 257 case SMTP_RCPT_TO:
meillo@80 258 {
meillo@80 259 char buf[MAX_ADDRESS];
meillo@80 260 address *addr;
meillo@80 261
meillo@80 262 if (!psc->helo_seen) {
meillo@80 263 smtp_printf(out, "503 need HELO or EHLO.\r\n");
meillo@80 264 break;
meillo@80 265 }
meillo@80 266 if (!psc->from_seen) {
meillo@80 267 smtp_printf(out, "503 need MAIL FROM: before RCPT TO:\r\n");
meillo@80 268 break;
meillo@80 269 }
meillo@136 270 if (!get_address(buffer, buf)) {
meillo@136 271 smtp_printf(out, "553 Address too long.\r\n");
meillo@136 272 break;
meillo@136 273 }
meillo@80 274
meillo@80 275 if (remote_host) {
meillo@80 276 addr = create_address(buf, TRUE);
meillo@80 277 } else {
meillo@80 278 addr = create_address_qualified(buf, TRUE, conf.host_name);
meillo@80 279 }
meillo@80 280 if (!addr) {
meillo@80 281 smtp_printf(out, "501 %s: syntax error in address.\r\n", buf);
meillo@80 282 break;
meillo@80 283 }
meillo@80 284 if (addr->local_part[0] == '|') {
meillo@80 285 smtp_printf(out, "501 %s: no pipe allowed for SMTP connections\r\n", buf);
meillo@80 286 break;
meillo@80 287 }
meillo@80 288 if (!addr->domain) {
meillo@80 289 smtp_printf(out, "501 recipient address must be qualified.\r\n", buf);
meillo@80 290 break;
meillo@80 291 }
meillo@80 292 gboolean do_relay = conf.do_relay;
meillo@80 293 if (!do_relay) {
meillo@80 294 do_relay = addr_is_local(msg->return_path);
meillo@80 295 if (!do_relay) {
meillo@80 296 do_relay = addr_is_local(addr);
meillo@80 297 }
meillo@80 298 }
meillo@80 299 if (!do_relay) {
meillo@80 300 smtp_printf(out, "550 relaying to %s denied.\r\n", addr_string(addr));
meillo@80 301 break;
meillo@80 302 }
meillo@80 303 psc->rcpt_seen = TRUE;
meillo@80 304 msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
meillo@80 305 smtp_printf(out, "250 OK %s is our friend.\r\n", addr->address);
meillo@80 306 }
meillo@10 307 break;
meillo@80 308
meillo@80 309 case SMTP_DATA:
meillo@80 310 if (!psc->helo_seen) {
meillo@80 311 smtp_printf(out, "503 need HELO or EHLO.\r\n");
meillo@80 312 break;
meillo@80 313 }
meillo@80 314 if (!psc->rcpt_seen) {
meillo@80 315 smtp_printf(out, "503 need RCPT TO: before DATA\r\n");
meillo@80 316 break;
meillo@80 317 }
meillo@80 318 accept_error err;
meillo@80 319
meillo@80 320 smtp_printf(out, "354 okay, and do not forget the dot\r\n");
meillo@80 321
meillo@80 322 err = accept_message(in, msg, conf.do_save_envelope_to ? ACC_SAVE_ENVELOPE_TO : 0);
meillo@80 323 if (err != AERR_OK) {
meillo@117 324 switch (err) {
meillo@117 325 case AERR_TIMEOUT:
meillo@117 326 case AERR_EOF:
meillo@117 327 return;
meillo@117 328 case AERR_SIZE:
meillo@117 329 smtp_printf(out, "552 Error: message too large.\r\n");
meillo@117 330 return;
meillo@117 331 default:
meillo@117 332 /* should never happen: */
meillo@117 333 smtp_printf(out, "451 Unknown error\r\n");
meillo@80 334 return;
meillo@80 335 }
meillo@80 336 }
meillo@80 337
meillo@80 338
meillo@80 339 if (!spool_write(msg, TRUE)) {
meillo@80 340 smtp_printf(out, "451 Could not write spool file\r\n");
meillo@80 341 return;
meillo@80 342 }
meillo@80 343 pid_t pid;
meillo@80 344 smtp_printf(out, "250 OK id=%s\r\n", msg->uid);
meillo@80 345
meillo@80 346 if (remote_host != NULL) {
meillo@80 347 logwrite(LOG_NOTICE, "%s <= <%s@%s> host=%s with %s\n", msg->uid,
meillo@80 348 msg->return_path->local_part, msg->return_path->domain,
meillo@80 349 remote_host, prot_names[psc->prot]);
meillo@80 350 } else {
meillo@80 351 logwrite(LOG_NOTICE, "%s <= <%s@%s> with %s\n", msg->uid,
meillo@80 352 msg->return_path->local_part, msg->return_path->domain,
meillo@80 353 prot_names[psc->prot]);
meillo@80 354 }
meillo@80 355
meillo@80 356 if (conf.do_queue) {
meillo@80 357 DEBUG(1) debugf("queuing forced by configuration or option.\n");
meillo@80 358 } else {
meillo@80 359 pid = fork();
meillo@80 360 if (pid == 0) {
meillo@80 361 _exit(deliver(msg));
meillo@80 362 } else if (pid < 0) {
meillo@208 363 logwrite(LOG_ALERT, "could not fork for delivery, id = %s\n", msg->uid);
meillo@80 364 }
meillo@80 365 }
meillo@80 366 psc->rcpt_seen = psc->from_seen = FALSE;
meillo@80 367 destroy_message(msg);
meillo@80 368 msg = NULL;
meillo@80 369 break;
meillo@80 370
meillo@80 371 case SMTP_QUIT:
meillo@80 372 smtp_printf(out, "221 goodbye\r\n");
meillo@81 373 destroy_message(msg);
meillo@81 374 msg = NULL;
meillo@80 375 return;
meillo@80 376
meillo@80 377 case SMTP_RSET:
meillo@80 378 psc->from_seen = psc->rcpt_seen = FALSE;
meillo@81 379 destroy_message(msg);
meillo@81 380 msg = NULL;
meillo@80 381 smtp_printf(out, "250 OK\r\n");
meillo@80 382 break;
meillo@80 383
meillo@80 384 case SMTP_NOOP:
meillo@80 385 smtp_printf(out, "250 OK\r\n");
meillo@80 386 break;
meillo@80 387
meillo@80 388 case SMTP_HELP:
meillo@80 389 {
meillo@80 390 int i;
meillo@80 391
meillo@80 392 smtp_printf(out, "214-supported commands:\r\n");
meillo@80 393 for (i = 0; i < SMTP_NUM_IDS - 1; i++) {
meillo@80 394 smtp_printf(out, "214-%s\r\n", smtp_cmds[i].cmd);
meillo@80 395 }
meillo@80 396 smtp_printf(out, "214 %s\r\n", smtp_cmds[i].cmd);
meillo@80 397 }
meillo@80 398 break;
meillo@80 399
meillo@10 400 default:
meillo@80 401 smtp_printf(out, "501 command not recognized\r\n");
meillo@80 402 DEBUG(1) debugf("command not recognized, was '%s'\n", buffer);
meillo@10 403 break;
meillo@10 404 }
meillo@0 405 }
meillo@80 406 switch (len) {
meillo@80 407 case -3:
meillo@80 408 logwrite(LOG_NOTICE, "connection timed out\n");
meillo@80 409 break;
meillo@80 410 case -2:
meillo@80 411 logwrite(LOG_NOTICE, "line overflow\n");
meillo@80 412 break;
meillo@80 413 case -1:
meillo@80 414 logwrite(LOG_NOTICE, "received EOF\n");
meillo@80 415 break;
meillo@80 416 default:
meillo@80 417 break;
meillo@80 418 }
meillo@0 419 }