/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "sendmail.h" #define LOGSENDINFO(buffer) if (get_verbose_mode()) { \ logmsg(LOG_INFO, "sendmail() : << %s", buffer); \ } #define SENDINFO(s, buffer) if (!socket_write(s, 0, "%s\r\n", buffer)) { \ logmsg(LOG_ERR, "sendmail() : %s", strerror(errno)); \ FREE(buffer); \ socket_destroy(s); \ return 0; \ } \ else { \ FREE(buffer); \ } static int g_sendmail_timeout = 2; static int g_max_timeout_number = 2; void set_sendmail_timeout(int second) { g_sendmail_timeout = second; } void set_max_timeout_number(int max) { g_max_timeout_number = max; } int get_helo(char **buffer) { char ptr_tmp[MAXHOSTNAMELEN]; size_t size; *buffer = strdup("EHLO "); if (*buffer == NULL) return 0; if (gethostname(ptr_tmp, MAXHOSTNAMELEN) == -1) return 0; else { size = strlen("EHLO ") + strlen(ptr_tmp) + 1; if (!saferealloc((void **)buffer, *buffer, size * sizeof(char))) { FREE(*buffer); return 0; } strlcat(*buffer, ptr_tmp, size * sizeof(char)); } return 1; } int get_authplain(char **buffer, const char *authuser, const char *authpass) { char *auth_64 = NULL; char *ptr_tmp = NULL; size_t ptr_size, s; int authuser_len = 0, authpass_len = 0; *buffer = strdup("AUTH PLAIN "); if (*buffer == NULL) return 0; ptr_size = strlen(authuser)*2 + 2 + strlen(authpass)+1; ptr_tmp = (char *)malloc(ptr_size * sizeof(char)); if (ptr_tmp == NULL) return 0; /* We prepare the AUTH string in base64 style */ /* \0\0 */ s = 0; authuser_len = strlen(authuser); authpass_len = strlen(authpass); memset(ptr_tmp, 0, ptr_size); memcpy(ptr_tmp, authuser, authuser_len * sizeof(char)); s += authuser_len; memcpy(&ptr_tmp[s], "\0", sizeof(char)); s += sizeof(char); memcpy(&ptr_tmp[s], authuser, authuser_len * sizeof(char)); s += authuser_len; memcpy(&ptr_tmp[s], "\0", sizeof(char)); s += sizeof(char); memcpy(&ptr_tmp[s], authpass, authpass_len * sizeof(char)); s += authpass_len; memcpy(&ptr_tmp[s], "\0", sizeof(char)); if (!base64_encode(&auth_64, ptr_tmp, ptr_size-1)) { FREE(ptr_tmp); return 0; } FREE(ptr_tmp); s = strlen("AUTH PLAIN ") + strlen(auth_64) + 1; if (!saferealloc((void **)buffer, *buffer, s * sizeof(char))) { FREE(*buffer); FREE(auth_64); return 0; } strlcat(*buffer, auth_64, s * sizeof(char)); FREE(auth_64); return 1; } int get_mailfrom(char **buffer, const char *mailfrom) { size_t size; *buffer = strdup("MAIL FROM: <"); if (*buffer == NULL) return 0; size = strlen("MAIL FROM: <") + strlen(mailfrom) + 2; if (!saferealloc((void **)buffer, *buffer, size * sizeof(char))) { FREE(*buffer); return 0; } strlcat(*buffer, mailfrom, size * sizeof(char)); strlcat(*buffer, ">", size * sizeof(char)); return 1; } int get_rcptto(char **buffer, const char *mailto) { size_t size; *buffer = strdup("RCPT TO: <"); if (*buffer == NULL) return 0; size = strlen("RCPT TO: <") + strlen(mailto) + 2; if (!saferealloc((void **)buffer, *buffer, size * sizeof(char))) { free(*buffer); *buffer = NULL; return 0; } strlcat(*buffer, mailto, size * sizeof(char)); strlcat(*buffer, ">", size * sizeof(char)); return 1; } char *SMTP_STATE_STRING(SMTP_STATE state) { switch(state) { case NOTHING: return "NOTHING"; break; case EHLO: return "EHLO"; break; case MAIL_FROM: return "MAIL_FROM"; break; case RCPT_TO: return "RCPT_TO"; break; case AUTH_PLAIN: return "AUTH_PLAIN"; break; case AUTH_LOGIN: return "AUTH_LOGIN"; break; case DATA: return "DATA"; break; case QUIT: return "QUIT"; break; } return NULL; } int sendmail(const char *host, const char *port, const char *mailfrom, const char *mailto, const char *authuser, const char *authpass, SMTP_AUTH auth, const char *additional_header, const char *msg) { /* S : we send R : We receive The following is a SMTP communication example R: 220 server.com Simple Mail Transfer Service Ready S: EHLO server.com [R: 250 server.com] -> no AUTH mecanism or [R: 250-server.com R: 250-PIPELINING R: 250-SIZE 10240000 R: 250-VRFY R: 250-ETRN R: 250-AUTH DIGEST-MD5 CRAM-MD5 GSSAPI PLAIN LOGIN -> AUTH mecanism R: 250-AUTH=DIGEST-MD5 CRAM-MD5 GSSAPI PLAIN LOGIN R: 250-XVERP R: 250 8BITMIME S: AUTH PLAIN dGVzdAB0ZXN0AHRlc3RwYXNz -> AUTH PLAIN mecanism R: 235 Authentication successful ] S: MAIL FROM: R: 250 OK S: RCPT TO: R: 250 OK S: DATA R: 354 Start mail input: end with . S: Type everything you want S: . R: 250 OK S: QUIT R: 250 server.com closing transmission channel */ Socket *s = NULL; fd_set fds; int n, byte_read, nb_timeout = 0; struct timeval tv; char buffer_in[255]; char *buffer_out = NULL; size_t size; SMTP_STATE state = NOTHING; s = socket_new(host, port, "tcp"); if (!socket_connect(s)) { logmsg(LOG_ERR, "sendmail() : connect() error on %s !", host); socket_destroy(s); return 0; } for(;;) { FD_ZERO(&fds); FD_SET(s->fd, &fds); tv.tv_sec = g_sendmail_timeout; tv.tv_usec = 0; if ((n = select(s->fd+1, &fds, NULL, NULL, &tv)) != -1) { if (n == 0) { /* select timeout */ nb_timeout++; logmsg(LOG_ERR, "sendmail() : select() timeout (%d time(s))", nb_timeout); if (g_max_timeout_number >= nb_timeout) { logmsg(LOG_ERR, "sendmail() : max number of select() timeout reached (%d time(s) max)", g_max_timeout_number); break; } } else { if (FD_ISSET(s->fd, &fds)) { nb_timeout = 0; memset(buffer_in, 0, 255); if ((byte_read = socket_readline(s, buffer_in, sizeof(buffer_in))) > 0) { stripcrlf(buffer_in); if (get_verbose_mode()) logmsg(LOG_INFO, "sendmail() : >> %s", buffer_in); if (strncmp(buffer_in, "220 ", strlen("220 ")) == 0) { /* 220 SMTP Server Name */ if (!get_helo(&buffer_out)) { logmsg(LOG_ERR, "sendmail() : problem in get_helo()"); socket_destroy(s); return 0; } LOGSENDINFO(buffer_out); SENDINFO(s, buffer_out); state = EHLO; } else if (strncmp(buffer_in, "250-AUTH ", strlen("250-AUTH ")) == 0) { /* 250-AUTH PLAIN LOGIN */ if ((authuser == NULL) || (authpass == NULL) || (auth == NONE)) { logmsg(LOG_ERR, "sendmail() : AUTH method required : set a username/password in your config file"); socket_destroy(s); return 0; } /* We check the LOGIN method, first */ if ((strstr(buffer_in, " LOGIN") != NULL) && (auth == LOGIN)) { state = AUTH_LOGIN; } else if ((strstr(buffer_in, " PLAIN") != NULL) && (auth == PLAIN)) { state = AUTH_PLAIN; } else { logmsg(LOG_ERR, "sendmail() : AUTH method other than LOGIN PLAIN, not supported yet"); socket_destroy(s); return 0; } } else if (strncmp(buffer_in, "250-", strlen("250-")) == 0) { /* We skip other 250 sub messages */ } else if (strncmp(buffer_in, "221 ", strlen("221 ")) == 0) { /* 221 Bye */ socket_destroy(s); return 1; /* We close the connection and it's OK */ } else if (strncmp(buffer_in, "235 ", strlen("235 ")) == 0) { /* 235 Authentication successful */ /* We send "MAIL FROM" command */ if (!get_mailfrom(&buffer_out, mailfrom)) { logmsg(LOG_ERR, "sendmail() : problem in get_mailfrom()"); socket_destroy(s); return 0; } LOGSENDINFO(buffer_out); SENDINFO(s, buffer_out); state = MAIL_FROM; } else if (strncmp(buffer_in, "250 ", strlen("250 ")) == 0) { /* 250 OK */ switch(state) { case EHLO: /* We send "MAIL FROM" command */ if (!get_mailfrom(&buffer_out, mailfrom)) { logmsg(LOG_ERR, "sendmail() : problem in get_mailfrom()"); socket_destroy(s); return 0; } LOGSENDINFO(buffer_out); SENDINFO(s, buffer_out); state = MAIL_FROM; break; case MAIL_FROM: /* We send "RCPT TO" command */ if (!get_rcptto(&buffer_out, mailto)) { logmsg(LOG_ERR, "sendmail() : problem in get_rcptto()"); socket_destroy(s); return 0; } LOGSENDINFO(buffer_out); SENDINFO(s, buffer_out); state = RCPT_TO; break; case RCPT_TO: /* We send "DATA" */ buffer_out = strdup("DATA"); LOGSENDINFO(buffer_out); SENDINFO(s, buffer_out); state = DATA; break; case DATA: /* We send "quit" */ buffer_out = strdup("QUIT"); LOGSENDINFO(buffer_out); SENDINFO(s, buffer_out); state = QUIT; break; case AUTH_PLAIN: /* We do AUTH PLAIN */ if (!get_authplain(&buffer_out, authuser, authpass)) { logmsg(LOG_ERR, "sendmail() : problem in get_authplain()"); socket_destroy(s); return 0; } LOGSENDINFO(buffer_out); SENDINFO(s, buffer_out); break; default: logmsg(LOG_ERR, "sendmail() : 250 : current state %s", SMTP_STATE_STRING(state)); socket_destroy(s); return 0; break; } } else if (strncmp(buffer_in, "354 ", strlen("354 ")) == 0) { /* 354 Start mail input: end with . */ switch(state) { case DATA: /* Send a proper header */ size = strlen("From: ") + strlen(mailfrom) + 2 /* \r\n */ + strlen("To: ") + strlen(mailto) + 2 /* \r\n */ + (additional_header != NULL ? strlen(additional_header) : 0) + 2 /* \r\n */ + 1; if (!saferealloc((void **)&buffer_out, buffer_out, size * sizeof(char))) { logmsg(LOG_ERR, "sendmail() : %s", strerror(errno)); socket_destroy(s); return 0; } *buffer_out = '\0'; strlcat(buffer_out, "From: ", size * sizeof(char)); strlcat(buffer_out, mailfrom, size * sizeof(char)); strlcat(buffer_out, "\r\n", size * sizeof(char)); strlcat(buffer_out, "To: ", size * sizeof(char)); strlcat(buffer_out, mailto, size * sizeof(char)); strlcat(buffer_out, "\r\n", size * sizeof(char)); if (additional_header != NULL) { strlcat(buffer_out, additional_header, size * sizeof(char)); strlcat(buffer_out, "\r\n", size * sizeof(char)); } SENDINFO(s, buffer_out); /* We can now send the body */ buffer_out = strdup(msg); size = strlen(msg) + strlen("\r\n.") + 1; if (!saferealloc((void **)&buffer_out, buffer_out, size * sizeof(char))) { logmsg(LOG_ERR, "sendmail() : %s", strerror(errno)); socket_destroy(s); return 0; } strlcat(buffer_out, "\r\n.", size * sizeof(char)); SENDINFO(s, buffer_out); break; default: logmsg(LOG_ERR, "sendmail() : 354 : current state %s", SMTP_STATE_STRING(state)); socket_destroy(s); return 0; break; } } else if (strncmp(buffer_in, "535 ", strlen("535 ")) == 0) { /* 535 Error: Authentication failed */ logmsg(LOG_ERR, "sendmail() : %s", buffer_in); break; } else { /* We have a SMTP error */ logmsg(LOG_ERR, "sendmail() : SMTP Error -> %s", buffer_in); break; } } else { if (errno != 0) logmsg(LOG_ERR, "sendmail() : readline error : %s (%d)", strerror(errno), errno); else logmsg(LOG_ERR, "sendmail() : readline error"); break; } } } } else { logmsg(LOG_ERR, "sendmail() : select() error"); break; } } socket_destroy(s); return 0; }