piler/src/misc.c

751 lines
14 KiB
C
Raw Normal View History

2011-11-14 15:57:52 +01:00
/*
* misc.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
#include <syslog.h>
#include <unistd.h>
#include <ctype.h>
2012-10-27 14:40:25 +02:00
#include <openssl/ssl.h>
2016-03-03 11:04:32 +01:00
#include <openssl/err.h>
2011-11-14 15:57:52 +01:00
#include "misc.h"
#include "smtpcodes.h"
#include "errmsg.h"
#include "config.h"
#include "tai.h"
2012-07-06 21:44:57 +02:00
int get_build(){
return BUILD;
}
2011-11-14 15:57:52 +01:00
void get_extractor_list(){
printf("Extractors: ");
#ifdef HAVE_PDFTOTEXT
printf("%s ", HAVE_PDFTOTEXT);
#endif
#ifdef HAVE_CATDOC
printf("%s ", HAVE_CATDOC);
#endif
#ifdef HAVE_CATPPT
printf("%s ", HAVE_CATPPT);
#endif
#ifdef HAVE_XLS2CSV
printf("%s ", HAVE_XLS2CSV);
#endif
#ifdef HAVE_PPTHTML
printf("%s ", HAVE_PPTHTML);
#endif
#ifdef HAVE_UNRTF
printf("%s ", HAVE_UNRTF);
#endif
#ifdef HAVE_TNEF
printf("%s ", HAVE_TNEF);
#endif
#ifdef HAVE_ZIP
printf("libzip ");
#endif
printf("\n\n");
}
2011-11-14 15:57:52 +01:00
void __fatal(char *s){
fprintf(stderr, "%s\n", s);
exit(1);
}
/*
* calculate the difference between two timevals in [usec]
2011-11-14 15:57:52 +01:00
*/
long tvdiff(struct timeval a, struct timeval b){
double res;
res = (a.tv_sec * 1000000 + a.tv_usec) - (b.tv_sec * 1000000 + b.tv_usec);
return (long) res;
}
/*
* split a string by a character as delimiter
*/
2014-07-22 16:00:03 +02:00
char *split(char *str, int ch, char *buf, int buflen, int *result){
char *p;
2011-11-14 15:57:52 +01:00
2014-07-22 16:00:03 +02:00
*result = 0;
2011-11-14 15:57:52 +01:00
2014-07-22 16:00:03 +02:00
if(str == NULL || buf == NULL || buflen < 2) return NULL;
p = strchr(str, ch);
if(p){
*p = '\0';
}
2011-11-14 15:57:52 +01:00
2014-07-22 16:00:03 +02:00
snprintf(buf, buflen, "%s", str);
2011-11-14 15:57:52 +01:00
2014-07-22 16:00:03 +02:00
if(p){
*p = ch;
*result = 1;
p++;
2011-11-14 15:57:52 +01:00
}
2014-07-22 16:00:03 +02:00
return p;
2011-11-14 15:57:52 +01:00
}
/*
* split a string by a string as delimiter
*/
char *split_str(char *row, char *what, char *s, int size){
char *r;
int len;
memset(s, 0, size);
if(row == NULL)
return NULL;
r = strstr(row, what);
if(r == NULL){
len = strlen(row);
if(len > size)
len = size;
}
else {
len = strlen(row) - strlen(r);
if(len > size)
len = size;
r += strlen(what);
}
strncpy(s, row, len);
s[len] = '\0';
2011-11-14 15:57:52 +01:00
return r;
}
/*
* trim trailing CR-LF
*/
2012-10-01 21:09:11 +02:00
int trimBuffer(char *s){
int n=0;
2011-11-14 15:57:52 +01:00
char *p;
p = strchr(s, '\n');
2012-10-01 21:09:11 +02:00
if(p){
*p = '\0';
n++;
}
2011-11-14 15:57:52 +01:00
p = strchr(s, '\r');
2012-10-01 21:09:11 +02:00
if(p){
*p = '\0';
n++;
}
2011-11-14 15:57:52 +01:00
2012-10-01 21:09:11 +02:00
return n;
2011-11-14 15:57:52 +01:00
}
2014-07-07 16:45:37 +02:00
int extract_verp_address(char *email){
char *p1;
2011-11-14 15:57:52 +01:00
2014-07-07 16:45:37 +02:00
// a VERP address is like archive+user=domain.com@myarchive.local
2014-07-07 16:45:37 +02:00
if(!email) return 0;
2014-06-30 16:10:36 +02:00
2014-07-07 16:45:37 +02:00
if(strlen(email) < 5) return 0;
p1 = strchr(email, '+');
if(p1){
char *p2 = strchr(p1, '@');
2014-07-07 16:45:37 +02:00
if(p2 && p2 > p1 + 2){
if(strchr(p1+1, '=')){
char puf[SMALLBUFSIZE];
2014-07-07 16:45:37 +02:00
memset(puf, 0, sizeof(puf));
memcpy(&puf[0], p1+1, p2-p1-1);
char *p = strchr(puf, '=');
2014-07-07 16:45:37 +02:00
if(p) *p = '@';
strcpy(email, puf);
2014-06-30 16:10:36 +02:00
}
}
2014-07-07 16:45:37 +02:00
}
return 0;
}
int extractEmail(char *rawmail, char *email){
char *p;
memset(email, 0, SMALLBUFSIZE);
p = strchr(rawmail, '<');
if(p){
2011-11-14 15:57:52 +01:00
snprintf(email, SMALLBUFSIZE-1, "%s", p+1);
p = strchr(email, '>');
if(p){
*p = '\0';
2014-07-07 16:45:37 +02:00
extract_verp_address(email);
2011-11-14 15:57:52 +01:00
return 1;
}
}
return 0;
}
/*
* Generate a random string from /dev/urandom or
* using the rand() function if not possible
*/
void make_random_string(unsigned char *buf, int buflen){
int i, len, fd;
int urandom=0;
static char alphanum[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
2013-09-11 09:19:29 +02:00
len = strlen(alphanum);
fd = open(RANDOM_POOL, O_RDONLY);
if(fd != -1){
if(readFromEntropyPool(fd, buf, buflen) == buflen){
for(i=0; i<buflen; i++){
*(buf+i) = alphanum[*(buf+i) % len];
}
urandom = 1;
}
close(fd);
}
if(urandom == 1) return;
2013-09-11 09:19:29 +02:00
for(i=0; i<buflen; i++){
*(buf+i) = alphanum[rand() % len];
}
}
2013-01-06 21:04:58 +01:00
void create_id(char *id, unsigned char server_id){
2011-11-14 15:57:52 +01:00
int i;
unsigned char buf[RND_STR_LEN/2];
memset(id, 0, SMALLBUFSIZE);
2013-01-06 21:04:58 +01:00
get_random_bytes(buf, RND_STR_LEN/2, server_id);
2011-11-14 15:57:52 +01:00
for(i=0; i < RND_STR_LEN/2; i++){
sprintf(id, "%02x", buf[i]);
id += 2;
}
}
/*
* reading from pool
*/
2013-01-06 21:04:58 +01:00
int get_random_bytes(unsigned char *buf, int len, unsigned char server_id){
2011-11-14 15:57:52 +01:00
int fd, ret=0;
struct taia now;
char nowpack[TAIA_PACK];
/* the first 12 bytes are the taia timestamp */
taia_now(&now);
taia_pack(nowpack, &now);
memcpy(buf, nowpack, 12);
fd = open(RANDOM_POOL, O_RDONLY);
if(fd == -1) return ret;
2013-01-06 21:04:58 +01:00
*(buf + 12) = server_id;
if(readFromEntropyPool(fd, buf+12+1, len-12-1) != len-12-1){
2011-11-14 15:57:52 +01:00
syslog(LOG_PRIORITY, "%s: %s", ERR_CANNOT_READ_FROM_POOL, RANDOM_POOL);
}
2011-11-14 15:57:52 +01:00
close(fd);
return ret;
}
/*
* read random data from entropy pool
*/
2016-04-05 21:10:09 +02:00
int readFromEntropyPool(int fd, void *_s, ssize_t n){
2011-11-14 15:57:52 +01:00
char *s = _s;
ssize_t pos = 0;
2011-11-14 15:57:52 +01:00
while(n > pos){
ssize_t res = read(fd, s + pos, n - pos);
2011-11-14 15:57:52 +01:00
switch(res){
case -1: continue;
case 0: return res;
default : pos += res;
}
}
return pos;
}
/*
* recv() with timeout
*/
int recvtimeout(int s, char *buf, int len, int timeout){
fd_set fds;
int n;
struct timeval tv;
2012-10-27 14:40:25 +02:00
memset(buf, 0, len);
2011-11-14 15:57:52 +01:00
FD_ZERO(&fds);
FD_SET(s, &fds);
tv.tv_sec = timeout;
tv.tv_usec = TIMEOUT_USEC;
n = select(s+1, &fds, NULL, NULL, &tv);
if (n == 0) return -2; // timeout!
if (n == -1) return -1; // error
return recv(s, buf, len, 0);
}
int write1(struct net *net, void *buf, int buflen){
2012-10-27 14:40:25 +02:00
int n;
if(net->use_ssl == 1)
n = SSL_write(net->ssl, buf, buflen);
2012-10-27 14:40:25 +02:00
else
n = send(net->socket, buf, buflen, 0);
2012-10-27 14:40:25 +02:00
return n;
}
2012-10-28 20:36:46 +01:00
int ssl_want_retry(SSL *ssl, int ret, int timeout){
int i;
fd_set fds;
struct timeval tv;
int sock;
// something went wrong. I'll retry, die quietly, or complain
i = SSL_get_error(ssl, ret);
if(i == SSL_ERROR_NONE)
return 1;
2012-10-28 20:36:46 +01:00
tv.tv_sec = timeout/1000;
tv.tv_usec = 0;
FD_ZERO(&fds);
2012-10-28 20:36:46 +01:00
switch(i){
case SSL_ERROR_WANT_READ: // pause until the socket is readable
sock = SSL_get_rfd(ssl);
FD_SET(sock, &fds);
i = select(sock+1, &fds, 0, 0, &tv);
break;
2012-10-28 20:36:46 +01:00
case SSL_ERROR_WANT_WRITE: // pause until the socket is writeable
sock = SSL_get_wfd(ssl);
FD_SET(sock, &fds);
i = select(sock+1, 0, &fds, 0, &tv);
break;
2012-10-28 20:36:46 +01:00
case SSL_ERROR_ZERO_RETURN: // the sock closed, just return quietly
i = 0;
break;
2012-10-28 20:36:46 +01:00
default: // ERROR - unexpected error code
i = -1;
break;
};
return i;
}
int ssl_read_timeout(SSL *ssl, void *buf, int len, int timeout){
int i;
while(1){
i = SSL_read(ssl, (char*)buf, len);
if(i > 0) break;
i = ssl_want_retry(ssl, i, timeout);
if(i <= 0) break;
}
return i;
}
int recvtimeoutssl(struct net *net, char *buf, int len){
2012-10-27 14:40:25 +02:00
memset(buf, 0, len);
if(net->use_ssl == 1){
return ssl_read_timeout(net->ssl, buf, len-1, net->timeout);
2012-10-27 14:40:25 +02:00
}
else {
return recvtimeout(net->socket, buf, len-1, net->timeout);
2012-10-27 14:40:25 +02:00
}
}
void close_connection(struct net *net){
close(net->socket);
2016-03-03 11:04:32 +01:00
if(net->use_ssl == 1){
SSL_shutdown(net->ssl);
SSL_free(net->ssl);
SSL_CTX_free(net->ctx);
2016-03-03 11:04:32 +01:00
ERR_free_strings();
}
}
2011-11-14 15:57:52 +01:00
void write_pid_file(char *pidfile){
FILE *f;
f = fopen(pidfile, "w");
if(f){
fprintf(f, "%d", (int)getpid());
fclose(f);
}
else syslog(LOG_PRIORITY, "cannot write pidfile: %s", pidfile);
}
int drop_privileges(struct passwd *pwd){
if(pwd->pw_uid > 0 && pwd->pw_gid > 0){
if(getgid() != pwd->pw_gid){
if(setgid(pwd->pw_gid)) return -1;
}
if(getuid() != pwd->pw_uid){
if(setuid(pwd->pw_uid)) return -1;
}
}
return 0;
}
void init_session_data(struct session_data *sdata, struct config *cfg){
int i;
sdata->fd = -1;
create_id(&(sdata->ttmpfile[0]), cfg->server_id);
unlink(sdata->ttmpfile);
snprintf(sdata->filename, SMALLBUFSIZE-1, "%s", sdata->ttmpfile);
snprintf(sdata->tmpframe, SMALLBUFSIZE-1, "%s.m", sdata->ttmpfile);
unlink(sdata->tmpframe);
memset(sdata->mailfrom, 0, SMALLBUFSIZE);
snprintf(sdata->client_addr, SMALLBUFSIZE-1, "null");
memset(sdata->attachments, 0, SMALLBUFSIZE);
memset(sdata->fromemail, 0, SMALLBUFSIZE);
sdata->duplicate_id = 0;
sdata->restored_copy = 0;
sdata->internal_sender = sdata->internal_recipient = sdata->external_recipient = 0;
sdata->direction = 0;
sdata->hdr_len = 0;
sdata->tot_len = 0;
2014-05-05 16:00:33 +02:00
sdata->stored_len = 0;
sdata->num_of_rcpt_to = 0;
2012-09-28 21:48:20 +02:00
sdata->ms_journal = 0;
sdata->journal_envelope_length = 0;
sdata->journal_bottom_length = 0;
sdata->tre = '-';
sdata->rav = AVIR_OK;
2012-01-26 14:35:51 +01:00
sdata->spam_message = 0;
sdata->customer_id = 0;
sdata->__acquire = sdata->__parsed = sdata->__av = sdata->__store = sdata->__compress = sdata->__encrypt = 0;
2014-01-13 13:06:10 +01:00
sdata->import = 0;
for(i=0; i<MAX_RCPT_TO; i++) memset(sdata->rcptto[i], 0, SMALLBUFSIZE);
time(&(sdata->now));
2015-11-10 16:06:47 +01:00
sdata->delivered = sdata->retained = sdata->now;
sdata->sent = 0;
2014-07-10 11:54:37 +02:00
sdata->sql_errno = 0;
#ifdef HAVE_TWEAK_SENT_TIME
sdata->sent += cfg->tweak_sent_time_offset;
#endif
}
int read_from_stdin(struct session_data *sdata){
int fd, rc=ERR, n;
char buf[MAXBUFSIZE];
fd = open(sdata->ttmpfile, O_CREAT|O_EXCL|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
if(fd == -1){
syslog(LOG_PRIORITY, "%s: cannot open ttmpfile", sdata->ttmpfile);
return rc;
}
while((n = read(0, buf, sizeof(buf))) > 0){
sdata->tot_len += write(fd, buf, n);
}
if(fsync(fd)){
syslog(LOG_PRIORITY, "%s: fsync() error", sdata->ttmpfile);
}
else rc = OK;
close(fd);
return rc;
}
void strtolower(char *s){
2014-08-30 21:10:29 +02:00
for(; *s; s++){
if(*s >= 65 && *s <= 90) *s = tolower(*s);
}
}
int make_socket_non_blocking(int fd){
int flags, s;
flags = fcntl(fd, F_GETFL, 0);
if(flags == -1){
return -1;
}
flags |= O_NONBLOCK;
s = fcntl(fd, F_SETFL, flags);
if(s == -1){
return -1;
}
return 0;
}
int create_and_bind(char *listen_addr, int listen_port){
struct addrinfo hints;
struct addrinfo *result, *rp;
char port_string[8];
int rc, fd;
memset(&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
snprintf(port_string, sizeof(port_string)-1, "%d", listen_port);
rc = getaddrinfo(listen_addr, port_string, &hints, &result);
if(rc != 0){
syslog(LOG_PRIORITY, "getaddrinfo for '%s': %s", listen_addr, gai_strerror(rc));
return -1;
}
for(rp = result; rp != NULL; rp = rp->ai_next){
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if(fd == -1)
continue;
rc = bind(fd, rp->ai_addr, rp->ai_addrlen);
if(rc == 0){
break;
}
close(fd);
}
if(rp == NULL){
syslog(LOG_PRIORITY, "cannot bind to port: %s:%d", listen_addr, listen_port);
return -1;
}
freeaddrinfo(result);
return fd;
}
int can_i_write_directory(char *dir){
int fd;
char filename[SMALLBUFSIZE];
if(dir)
snprintf(filename, sizeof(filename)-1, "%s/__piler_%d", dir, getpid());
else
snprintf(filename, sizeof(filename)-1, "__piler_%d", getpid());
fd = open(filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
if(fd == -1){
return 0;
}
close(fd);
unlink(filename);
return 1;
}
void move_email(struct smtp_session *session){
char buf[SMALLBUFSIZE];
snprintf(buf, sizeof(buf)-1, "%d/%s", session->ttmpfile[0] % session->cfg->number_of_worker_processes, session->ttmpfile);
if(rename(session->ttmpfile, buf)){
syslog(LOG_PRIORITY, "ERROR: couldn't rename %s to %s (reason: %s)", session->ttmpfile, buf, strerror(errno));
}
}
int read_one_line(char *s, int c, char *buf, int buflen, int *rc){
int i=0;
*rc = ERR;
memset(buf, 0, buflen);
if(s == NULL){
return i;
}
for(; *s; s++){
if(i<buflen-2){
buf[i] = *s;
i++;
if(*s == c){
*rc = OK;
break;
}
}
else break;
}
return i;
}
int init_ssl_to_server(struct data *data){
int n;
X509* server_cert;
char *str;
SSL_library_init();
SSL_load_error_strings();
#if OPENSSL_VERSION_NUMBER < 0x10100000L
data->net->ctx = SSL_CTX_new(TLSv1_2_client_method());
#else
data->net->ctx = SSL_CTX_new(TLS_client_method());
#endif
CHK_NULL(data->net->ctx, "internal SSL error");
data->net->ssl = SSL_new(data->net->ctx);
CHK_NULL(data->net->ssl, "internal ssl error");
SSL_set_fd(data->net->ssl, data->net->socket);
n = SSL_connect(data->net->ssl);
CHK_SSL(n, "internal ssl error");
printf("Cipher: %s\n", SSL_get_cipher(data->net->ssl));
server_cert = SSL_get_peer_certificate(data->net->ssl);
CHK_NULL(server_cert, "server cert error");
str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0);
CHK_NULL(str, "error in server cert");
OPENSSL_free(str);
str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0);
CHK_NULL(str, "error in server cert");
OPENSSL_free(str);
X509_free(server_cert);
return OK;
}
2011-11-14 15:57:52 +01:00
#ifndef _GNU_SOURCE
char *strcasestr(const char *s, const char *find){
char c, sc;
size_t len;
if((c = *find++) != 0){
c = tolower((unsigned char)c);
len = strlen(find);
do {
do {
if((sc = *s++) == 0)
return (NULL);
} while((char)tolower((unsigned char)sc) != c);
} while (strncasecmp(s, find, len) != 0);
s--;
}
return((char*)s);
}
#endif