masqmail

annotate src/smtp_in.c @ 156:ee2afbf92428

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