405 lines
10 KiB
C
405 lines
10 KiB
C
#include <stdlib.h>
|
|
#include <syslog.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/select.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <syslog.h>
|
|
#include <string.h>
|
|
|
|
#include "smtp.h"
|
|
|
|
|
|
|
|
#define BUFSIZE 1024
|
|
#define BUFSIZE2 1024
|
|
|
|
|
|
|
|
smtp_t *smtp_init(unsigned int address, int port, int timeout) {
|
|
smtp_t *handle = (smtp_t*) malloc(sizeof(smtp_t));
|
|
|
|
handle->address = address;
|
|
handle->port = port;
|
|
handle->timeout = timeout;
|
|
handle->response_text = NULL;
|
|
handle->response_code = 0;
|
|
handle->got_timeout = 0;
|
|
|
|
handle->buffer = (char*) malloc(BUFSIZE+5);
|
|
|
|
return handle;
|
|
}
|
|
|
|
smtp_t *smtp_init2(char *host_address, int port, int timeout) {
|
|
unsigned int a = inet_addr(host_address);
|
|
if (-1 == a) {
|
|
syslog(LOG_DEBUG, "smtp_init2: invalid host_address %s", host_address);
|
|
return NULL;
|
|
}
|
|
|
|
return smtp_init(a, port, timeout);
|
|
}
|
|
|
|
void smtp_destroy(smtp_t *handle) {
|
|
free(handle->buffer);
|
|
free(handle);
|
|
}
|
|
|
|
static int _smtp_strip_crlf(char *s) {
|
|
char *c;
|
|
int cnt = 0;
|
|
|
|
if ((10 == s[strlen(s)-1]) && (13 == s[strlen(s)-2]))
|
|
s[strlen(s)-2] = '\0';
|
|
|
|
/* for (c = s; *c != '\0'; c++) { */
|
|
/* if ((10 == *c) || (13 == *c)) { */
|
|
/* *c = 'X'; */
|
|
/* cnt++; */
|
|
/* } */
|
|
/* } */
|
|
|
|
return cnt;
|
|
}
|
|
|
|
|
|
static int _smtp_read(smtp_t *handle) {
|
|
int res;
|
|
fd_set rdfs;
|
|
struct timeval tv;
|
|
|
|
FD_ZERO(&rdfs);
|
|
FD_SET(handle->socket, &rdfs);
|
|
tv.tv_sec = handle->timeout;
|
|
tv.tv_usec = 0;
|
|
|
|
res = select(handle->socket+1, &rdfs, NULL, NULL, &tv);
|
|
if (0 == res) {
|
|
syslog(LOG_DEBUG, "_smtp_read: timeout in select");
|
|
handle->got_timeout = 1;
|
|
return SMTP_TIMEOUT;
|
|
} else if (-1 == res) {
|
|
syslog(LOG_DEBUG, "_smtp_read: error in select, errno: %d (%s)", errno, strerror(errno));
|
|
return -1;
|
|
} else {
|
|
if (FD_ISSET(handle->socket, &rdfs)) {
|
|
res = read(handle->socket, handle->buffer, BUFSIZE);
|
|
if (-1 == res) {
|
|
syslog(LOG_DEBUG, "read error: %d, %s\n", errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
handle->buffer[res] = '\0';
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int _smtp_write(smtp_t *handle) {
|
|
int res;
|
|
fd_set wrfs;
|
|
struct timeval tv;
|
|
|
|
FD_ZERO(&wrfs);
|
|
FD_SET(handle->socket, &wrfs);
|
|
tv.tv_sec = handle->timeout;
|
|
tv.tv_usec = 0;
|
|
|
|
res = select(handle->socket+1, NULL, &wrfs, NULL, &tv);
|
|
if (0 == res) {
|
|
syslog(LOG_DEBUG, "_smtp_write: timeout in select");
|
|
handle->got_timeout = 1;
|
|
return SMTP_TIMEOUT;
|
|
} else if (-1 == res) {
|
|
syslog(LOG_DEBUG, "_smtp_write: error in select, errno: %d (%s)", errno, strerror(errno));
|
|
return -1;
|
|
} else {
|
|
if (FD_ISSET(handle->socket, &wrfs)) {
|
|
if (-1 == write(handle->socket, handle->buffer, strlen(handle->buffer))) {
|
|
syslog(LOG_DEBUG, "read error: %d, %s\n", errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int _smtp_parse_response(smtp_t *handle) {
|
|
char *first_space;
|
|
char *last_crlf;
|
|
|
|
int l;
|
|
|
|
_smtp_strip_crlf(handle->buffer);
|
|
|
|
|
|
if (NULL == (last_crlf = strrchr(handle->buffer, 10)))
|
|
last_crlf = handle->buffer;
|
|
|
|
if (NULL == (first_space = strchr(last_crlf, ' ')))
|
|
return -1;
|
|
|
|
*first_space = '\0';
|
|
handle->response_code = atoi(last_crlf);
|
|
|
|
*first_space = ' ';
|
|
handle->response_text = handle->buffer;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int smtp_response(smtp_t *handle, char **response) {
|
|
if (handle->got_timeout) {
|
|
handle->got_timeout = 0;
|
|
return SMTP_TIMEOUT;
|
|
}
|
|
|
|
if (0 != _smtp_parse_response(handle)) {
|
|
syslog(LOG_DEBUG, "smtp_response: failed to parse response\n");
|
|
return -1;
|
|
}
|
|
|
|
if (NULL != response)
|
|
*response = handle->response_text;
|
|
return handle->response_code;
|
|
}
|
|
|
|
int smtp_command(smtp_t *handle, char *command, char *arg) {
|
|
int err;
|
|
|
|
*handle->buffer = '\0';
|
|
strcat(handle->buffer, command);
|
|
if (NULL != arg) {
|
|
/* strcat(handle->buffer, " "); */
|
|
strcat(handle->buffer, arg);
|
|
}
|
|
strcat(handle->buffer, "\r\n");
|
|
|
|
if (0 != (err = _smtp_write(handle))) {
|
|
syslog(LOG_DEBUG, "smtp_command: failed to send %s\n", handle->buffer);
|
|
return err;
|
|
}
|
|
|
|
if (0 != (err = _smtp_read(handle))) {
|
|
syslog(LOG_DEBUG, "smtp_command: failed to read response\n");
|
|
return err;
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int smtp_connect(smtp_t *handle) {
|
|
int err;
|
|
int c;
|
|
int res;
|
|
fd_set wrfs;
|
|
struct timeval tv;
|
|
|
|
|
|
handle->socket = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (-1 == handle->socket) {
|
|
syslog(LOG_DEBUG, "smtp_connect: unable to create socket");
|
|
return -1;
|
|
}
|
|
|
|
c = fcntl(handle->socket, F_GETFL);
|
|
if (-1 == c) {
|
|
syslog(LOG_DEBUG, "smtp_connect: unable to get flags, errno: %d (%s)", errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
err = fcntl(handle->socket, F_SETFL, c | O_NONBLOCK);
|
|
if (-1 == err) {
|
|
syslog(LOG_DEBUG, "smtp_connect: unable to set O_NONBLOCK flag, errno: %d (%s)", errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
|
|
memset(&handle->addr, 0, sizeof(handle->addr));
|
|
handle->addr.sin_addr.s_addr = handle->address;
|
|
|
|
handle->addr.sin_port = htons(handle->port);
|
|
handle->addr.sin_family = AF_INET;
|
|
|
|
c = connect(handle->socket, (struct sockaddr *) &handle->addr, sizeof(handle->addr));
|
|
if (-1 == c) {
|
|
if (EINPROGRESS == errno) {
|
|
FD_ZERO(&wrfs);
|
|
FD_SET(handle->socket, &wrfs);
|
|
tv.tv_sec = handle->timeout;
|
|
tv.tv_usec = 0;
|
|
|
|
res = select(handle->socket+1, NULL, &wrfs, NULL, &tv);
|
|
if (0 == res) {
|
|
syslog(LOG_DEBUG, "smtp_connect: timeout in select");
|
|
handle->got_timeout = 1;
|
|
return SMTP_TIMEOUT;
|
|
} else if (-1 == res) {
|
|
syslog(LOG_DEBUG, "smtp_connect: error in select, errno: %d (%s)", errno, strerror(errno));
|
|
return -1;
|
|
} else {
|
|
if (FD_ISSET(handle->socket, &wrfs)) {
|
|
// debug("smtp_connect: here we go");
|
|
}
|
|
}
|
|
} else {
|
|
syslog(LOG_DEBUG, "smtp_connect: unable to connect to socket, errno: %d (%s)", errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
err = _smtp_read(handle);
|
|
if (0 != err)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int smtp_helo(smtp_t *handle, char *arg) {
|
|
return smtp_command(handle, "helo ", arg);
|
|
}
|
|
|
|
int smtp_ehlo(smtp_t *handle, char *arg) {
|
|
return smtp_command(handle, "ehlo ", arg);
|
|
}
|
|
|
|
int smtp_lhlo(smtp_t *handle, char *arg) {
|
|
return smtp_command(handle, "lhlo ", arg);
|
|
}
|
|
|
|
int smtp_mailfrom(smtp_t *handle, char *arg) {
|
|
return smtp_command(handle, "mail from:", arg);
|
|
}
|
|
|
|
int smtp_rcptto(smtp_t *handle, char *arg) {
|
|
return smtp_command(handle, "rcpt to:", arg);
|
|
}
|
|
|
|
int smtp_data(smtp_t *handle) {
|
|
return smtp_command(handle, "data", NULL);
|
|
}
|
|
|
|
|
|
int smtp_datasend(smtp_t *handle, char *data) {
|
|
char *d = data;
|
|
int cnt;
|
|
int ssize;
|
|
|
|
while (d < data + strlen(data)) {
|
|
ssize = (strlen(d) < BUFSIZE2) ? strlen(d) : BUFSIZE2;
|
|
if (-1 == write(handle->socket, d, ssize))
|
|
return -1;
|
|
d += BUFSIZE2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int smtp_dataend(smtp_t *handle) {
|
|
return smtp_command(handle, "\r\n.", NULL);
|
|
}
|
|
|
|
int smtp_rset(smtp_t *handle) {
|
|
return smtp_command(handle, "rset", NULL);
|
|
}
|
|
|
|
int smtp_quit(smtp_t *handle) {
|
|
return smtp_command(handle, "quit", NULL);
|
|
}
|
|
|
|
int smtp_close(smtp_t *handle) {
|
|
close(handle->socket);
|
|
}
|
|
|
|
|
|
#ifdef _TEST_MODE_
|
|
int main() {
|
|
int res;
|
|
char *res_text;
|
|
char data[] = "Hallo Wolfgang\r\n"
|
|
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"abaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaadaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaafaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaagaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaahaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaajaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaakaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaalaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaaamaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaaaanaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaaaaaoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaaaaaapaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaaaaaaaqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaaaaaaaaraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaaaaaaaaasaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaaaaaaaaaataaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaaaaaaaaaaauaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaaaaaaaaaaaavaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaaaaaaaaaaaaawaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"aaaaaaaaaaaaaaaaaaaaaaaxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
|
|
"\r\n"
|
|
"dies ist eine Testmail.\r\n"
|
|
"\r\n";
|
|
|
|
|
|
smtp_t *h = smtp_init2("213.70.191.212", 25, 10);
|
|
printf("got handle\n");
|
|
|
|
res = smtp_connect(h);
|
|
printf("connected: %d\n", res);
|
|
|
|
res = smtp_response(h, &res_text);
|
|
printf("response: %d, %s\n", res, res_text);
|
|
|
|
res = smtp_helo(h, "local");
|
|
printf("ehlo: %d\n", res);
|
|
|
|
res = smtp_response(h, &res_text);
|
|
printf("response: %d, %s\n", res, res_text);
|
|
|
|
res = smtp_mailfrom(h, "<xyx@test.de>");
|
|
printf("mail from: %d\n", res);
|
|
|
|
res = smtp_response(h, &res_text);
|
|
printf("response: %d, %s\n", res, res_text);
|
|
|
|
res = smtp_rcptto(h, "<wn@kja-essen.de>");
|
|
printf("rcpt to: %d\n", res);
|
|
|
|
res = smtp_response(h, &res_text);
|
|
printf("response: %d, %s\n", res, res_text);
|
|
|
|
res = smtp_data(h);
|
|
printf("data: %d\n", res);
|
|
|
|
res = smtp_response(h, &res_text);
|
|
printf("response: %d, %s\n", res, res_text);
|
|
|
|
res = smtp_datasend(h, data);
|
|
printf("datasend: %d\n", res);
|
|
|
|
res = smtp_dataend(h);
|
|
printf("dataend: %d\n", res);
|
|
|
|
res = smtp_response(h, &res_text);
|
|
printf("response: %d, %s\n", res, res_text);
|
|
|
|
|
|
smtp_close(h);
|
|
smtp_destroy(h);
|
|
}
|
|
|
|
#endif /* _TEST_MODE_ */
|
|
|