From 2a94804586d2e9409060cd9818e4dc415c751fa4 Mon Sep 17 00:00:00 2001 From: SJ Date: Tue, 9 Apr 2013 14:50:27 +0200 Subject: [PATCH] added pilergetd daemon --- configure | 2 +- configure.in | 2 +- src/Makefile.in | 8 +- src/archive.c | 76 +++++++++ src/cfg.c | 3 + src/cfg.h | 4 + src/config.h | 4 +- src/imap.c | 10 +- src/misc.c | 12 +- src/misc.h | 4 +- src/piler.c | 26 +--- src/piler.h | 2 + src/pilergetd.c | 399 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pop3.c | 10 +- src/retr.c | 251 ++++++++++++++++++++++++++++++ src/session.c | 12 +- src/sig.c | 14 ++ src/sig.h | 1 + 18 files changed, 787 insertions(+), 53 deletions(-) create mode 100644 src/pilergetd.c create mode 100644 src/retr.c diff --git a/configure b/configure index 7193cb9e..b849d4ad 100755 --- a/configure +++ b/configure @@ -4634,7 +4634,7 @@ echo; echo CFLAGS="$static -O2 -Wall -g" LIBS="$antispam_libs $sunos_libs " -OBJS="dirs.o base64.o misc.o counters.o cfg.o sig.o decoder.o list.o parser.o parser_utils.o rules.o session.o message.o attachment.o digest.o store.o archive.o tai.o import.o imap.o pop3.o extract.o mydomains.o mysql.o $objs" +OBJS="dirs.o base64.o misc.o counters.o cfg.o sig.o decoder.o list.o parser.o parser_utils.o rules.o session.o message.o attachment.o digest.o store.o archive.o tai.o import.o imap.o pop3.o extract.o mydomains.o retr.o mysql.o $objs" ac_config_files="$ac_config_files Makefile src/Makefile etc/Makefile util/Makefile init.d/Makefile test/Makefile contrib/imap/Makefile" diff --git a/configure.in b/configure.in index 0ae71afa..97c7a136 100644 --- a/configure.in +++ b/configure.in @@ -405,7 +405,7 @@ echo; echo CFLAGS="$static -O2 -Wall -g" LIBS="$antispam_libs $sunos_libs " -OBJS="dirs.o base64.o misc.o counters.o cfg.o sig.o decoder.o list.o parser.o parser_utils.o rules.o session.o message.o attachment.o digest.o store.o archive.o tai.o import.o imap.o pop3.o extract.o mydomains.o mysql.o $objs" +OBJS="dirs.o base64.o misc.o counters.o cfg.o sig.o decoder.o list.o parser.o parser_utils.o rules.o session.o message.o attachment.o digest.o store.o archive.o tai.o import.o imap.o pop3.o extract.o mydomains.o retr.o mysql.o $objs" AC_CONFIG_FILES([Makefile src/Makefile etc/Makefile util/Makefile init.d/Makefile test/Makefile contrib/imap/Makefile]) AC_OUTPUT diff --git a/src/Makefile.in b/src/Makefile.in index 49429b64..df72f90f 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -33,13 +33,16 @@ MAKE = `which make` INSTALL = @INSTALL@ -all: libpiler.a piler pilerconf pilerget pileraget pilerimport pilerexport pilerpurge reindex test +all: libpiler.a piler pilerconf pilerget pilergetd pileraget pilerimport pilerexport pilerpurge reindex test install: install-piler piler: piler.c libpiler.a $(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $< -lpiler $(LIBS) $(LIBDIR) @LDFLAGS@ @libclamav_extra_libs@ +pilergetd: pilergetd.c libpiler.a + $(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $< -lpiler $(LIBS) $(LIBDIR) @LDFLAGS@ @libclamav_extra_libs@ + libpiler.a: $(OBJS) $(MYSQL_OBJS) ar cr libpiler.a $(OBJS) $(MYSQL_OBJS) ranlib libpiler.a @@ -83,6 +86,7 @@ install-piler: (cd $(DESTDIR)$(libdir) && ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION)) $(INSTALL) -m 0755 piler $(DESTDIR)$(sbindir) + $(INSTALL) -m 0755 pilergetd $(DESTDIR)$(sbindir) $(INSTALL) -m 0755 pilerconf $(DESTDIR)$(sbindir) $(INSTALL) -m 6755 -o $(RUNNING_USER) -g $(RUNNING_GROUP) pilerget $(DESTDIR)$(bindir) $(INSTALL) -m 6755 -o $(RUNNING_USER) -g $(RUNNING_GROUP) pileraget $(DESTDIR)$(bindir) @@ -92,7 +96,7 @@ install-piler: $(INSTALL) -m 6755 -o $(RUNNING_USER) -g $(RUNNING_GROUP) reindex $(DESTDIR)$(bindir) clean: - rm -f *.o *.a libpiler.so* piler pilerconf pilerget pileraget pilerimport pilerexport pilerpurge pilertest reindex + rm -f *.o *.a libpiler.so* piler pilerconf pilerget pilergetd pileraget pilerimport pilerexport pilerpurge pilertest reindex distclean: clean rm -f Makefile diff --git a/src/archive.c b/src/archive.c index 0d4d74f4..b1bf9aa3 100644 --- a/src/archive.c +++ b/src/archive.c @@ -209,6 +209,82 @@ CLEANUP: } +int file_from_archive_to_network(char *filename, int sd, struct __data *data, struct __config *cfg){ + int n, olen, tlen, len, fd=-1; + unsigned char *s=NULL, *addr=NULL, inbuf[REALLYBIGBUFSIZE]; + struct stat st; + EVP_CIPHER_CTX ctx; + + + if(filename == NULL) return 1; + + + fd = open(filename, O_RDONLY); + if(fd == -1){ + syslog(LOG_PRIORITY, "%s: cannot open()", filename); + return 1; + } + + + if(fstat(fd, &st)){ + syslog(LOG_PRIORITY, "%s: cannot fstat()", filename); + close(fd); + return 1; + } + + + if(cfg->encrypt_messages == 1){ + EVP_CIPHER_CTX_init(&ctx); + EVP_DecryptInit_ex(&ctx, EVP_bf_cbc(), NULL, cfg->key, cfg->iv); + + len = st.st_size+EVP_MAX_BLOCK_LENGTH; + + s = malloc(len); + + if(!s){ + syslog(LOG_PRIORITY, "error: malloc(), '%s'", filename); + goto CLEANUP2; + } + + tlen = 0; + + while((n = read(fd, inbuf, sizeof(inbuf)))){ + + if(!EVP_DecryptUpdate(&ctx, s+tlen, &olen, inbuf, n)){ + syslog(LOG_PRIORITY, "%s: EVP_DecryptUpdate()", filename); + goto CLEANUP2; + } + + tlen += olen; + } + + + if(EVP_DecryptFinal(&ctx, s + tlen, &olen) != 1){ + syslog(LOG_PRIORITY, "%s: EVP_DecryptFinal()", filename); + goto CLEANUP2; + } + + + tlen += olen; + write1(sd, s, tlen, 1, data->ssl); + + } + else { + addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + write1(sd, addr, st.st_size, 1, data->ssl); + munmap(addr, st.st_size); + } + + +CLEANUP2: + if(fd != -1) close(fd); + if(s) free(s); + if(cfg->encrypt_messages == 1) EVP_CIPHER_CTX_cleanup(&ctx); + + return 0; +} + + int retrieve_email_from_archive(struct session_data *sdata, struct __data *data, FILE *dest, struct __config *cfg){ int i, attachments; char *buffer=NULL, *saved_buffer, *p, filename[SMALLBUFSIZE], pointer[SMALLBUFSIZE]; diff --git a/src/cfg.c b/src/cfg.c index 2c7e8bc8..620c2e27 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -90,6 +90,9 @@ struct _parse_rule config_parse_rules[] = { "pemfile", "string", (void*) string_parser, offsetof(struct __config, pemfile), "", MAXVAL-1}, { "pidfile", "string", (void*) string_parser, offsetof(struct __config, pidfile), PIDFILE, MAXVAL-1}, { "piler_header_field", "string", (void*) string_parser, offsetof(struct __config, piler_header_field), "", MAXVAL-1}, + { "pilergetd_listen_addr", "string", (void*) string_parser, offsetof(struct __config, pilergetd_listen_addr), "127.0.0.1", MAXVAL-1}, + { "pilergetd_listen_port", "integer", (void*) int_parser, offsetof(struct __config, pilergetd_listen_port), "10026", sizeof(int)}, + { "pilergetd_pidfile", "string", (void*) string_parser, offsetof(struct __config, pilergetd_pidfile), PILERGETD_PIDFILE, MAXVAL-1}, { "queuedir", "string", (void*) string_parser, offsetof(struct __config, queuedir), QUEUE_DIR, MAXVAL-1}, { "server_id", "integer", (void*) int_parser, offsetof(struct __config, server_id), "0", sizeof(int)}, { "session_timeout", "integer", (void*) int_parser, offsetof(struct __config, session_timeout), "420", sizeof(int)}, diff --git a/src/cfg.h b/src/cfg.h index 09e23946..d0bc0492 100644 --- a/src/cfg.h +++ b/src/cfg.h @@ -13,10 +13,14 @@ struct __config { char hostid[MAXVAL]; char pidfile[MAXVAL]; + char pilergetd_pidfile[MAXVAL]; char listen_addr[MAXVAL]; int listen_port; + char pilergetd_listen_addr[MAXVAL]; + int pilergetd_listen_port; + char clamd_addr[MAXVAL]; int clamd_port; char clamd_socket[MAXVAL]; diff --git a/src/config.h b/src/config.h index 72063f94..3ee240df 100644 --- a/src/config.h +++ b/src/config.h @@ -12,9 +12,9 @@ #define PROGNAME "piler" #define PILERGETD_PROGNAME "pilergetd" -#define VERSION "0.1.23" +#define VERSION "0.1.24-master-branch" -#define BUILD 789 +#define BUILD 791 #define HOSTID "mailarchiver" diff --git a/src/imap.c b/src/imap.c index d3590593..43f98b88 100644 --- a/src/imap.c +++ b/src/imap.c @@ -81,7 +81,7 @@ int process_imap_folder(int sd, int *seq, char *folder, struct session_data *sda snprintf(tag, sizeof(tag)-1, "A%d", *seq); snprintf(tagok, sizeof(tagok)-1, "\r\nA%d OK", (*seq)++); snprintf(buf, sizeof(buf)-1, "%s SELECT \"%s\"\r\n", tag, folder); - n = write1(sd, buf, use_ssl, data->ssl); + n = write1(sd, buf, strlen(buf), use_ssl, data->ssl); n = recvtimeoutssl(sd, buf, sizeof(buf), 10, use_ssl, data->ssl); if(!strstr(buf, tagok)){ @@ -124,7 +124,7 @@ int process_imap_folder(int sd, int *seq, char *folder, struct session_data *sda } - n = write1(sd, buf, use_ssl, data->ssl); + n = write1(sd, buf, strlen(buf), use_ssl, data->ssl); readlen = 0; pos = 0; @@ -240,14 +240,14 @@ int connect_to_imap_server(int sd, int *seq, char *username, char *password, int snprintf(tag, sizeof(tag)-1, "A%d", *seq); snprintf(tagok, sizeof(tagok)-1, "A%d OK", (*seq)++); snprintf(buf, sizeof(buf)-1, "%s CAPABILITY\r\n", tag); - write1(sd, buf, use_ssl, data->ssl); + write1(sd, buf, strlen(buf), use_ssl, data->ssl); n = recvtimeoutssl(sd, buf, sizeof(buf), 10, use_ssl, data->ssl); snprintf(tag, sizeof(tag)-1, "A%d", *seq); snprintf(tagok, sizeof(tagok)-1, "A%d OK", (*seq)++); snprintf(buf, sizeof(buf)-1, "%s LOGIN %s \"%s\"\r\n", tag, username, password); - write1(sd, buf, use_ssl, data->ssl); + write1(sd, buf, strlen(buf), use_ssl, data->ssl); n = recvtimeoutssl(sd, buf, sizeof(buf), 10, use_ssl, data->ssl); if(strncmp(buf, tagok, strlen(tagok))){ @@ -281,7 +281,7 @@ int list_folders(int sd, int *seq, char *folders, int foldersize, int use_ssl, s //snprintf(puf, sizeof(puf)-1, "%s LIST \"\" %%\r\n", tag); snprintf(puf, sizeof(puf)-1, "%s LIST \"\" \"*\"\r\n", tag); - write1(sd, puf, use_ssl, data->ssl); + write1(sd, puf, strlen(puf), use_ssl, data->ssl); while(1){ n = recvtimeoutssl(sd, puf, sizeof(puf), 10, use_ssl, data->ssl); diff --git a/src/misc.c b/src/misc.c index 27652a84..8a7c74e1 100644 --- a/src/misc.c +++ b/src/misc.c @@ -302,13 +302,13 @@ int recvtimeout(int s, char *buf, int len, int timeout){ } -int write1(int sd, char *buf, int use_ssl, SSL *ssl){ +int write1(int sd, void *buf, int buflen, int use_ssl, SSL *ssl){ int n; if(use_ssl == 1) - n = SSL_write(ssl, buf, strlen(buf)); + n = SSL_write(ssl, buf, buflen); else - n = send(sd, buf, strlen(buf), 0); + n = send(sd, buf, buflen, 0); return n; } @@ -520,6 +520,12 @@ void strtolower(char *s){ } +void *get_in_addr(struct sockaddr *sa){ + if(sa->sa_family == AF_INET) return &(((struct sockaddr_in*)sa)->sin_addr); + return &(((struct sockaddr_in6*)sa)->sin6_addr); +} + + #ifndef _GNU_SOURCE char *strcasestr(const char *s, const char *find){ char c, sc; diff --git a/src/misc.h b/src/misc.h index fe93ed22..5bffa9f5 100644 --- a/src/misc.h +++ b/src/misc.h @@ -28,7 +28,7 @@ void create_id(char *id, unsigned char server_id); int get_random_bytes(unsigned char *buf, int len, unsigned char server_id); int readFromEntropyPool(int fd, void *_s, size_t n); int recvtimeout(int s, char *buf, int len, int timeout); -int write1(int sd, char *buf, int use_ssl, SSL *ssl); +int write1(int sd, void *buf, int buflen, int use_ssl, SSL *ssl); int recvtimeoutssl(int s, char *buf, int len, int timeout, int use_ssl, SSL *ssl); void write_pid_file(char *pidfile); @@ -39,6 +39,8 @@ void init_session_data(struct session_data *sdata, struct __config *cfg); int read_from_stdin(struct session_data *sdata); void strtolower(char *s); +void *get_in_addr(struct sockaddr *sa); + #ifndef _GNU_SOURCE char *strcasestr(const char *s, const char *find); #endif diff --git a/src/piler.c b/src/piler.c index 5e2db334..85136f3c 100644 --- a/src/piler.c +++ b/src/piler.c @@ -41,7 +41,6 @@ struct passwd *pwd; struct child children[MAXCHILDREN]; -signal_func *set_signal_handler(int signo, signal_func * func); static void takesig(int sig); static void child_sighup_handler(int sig); static void child_main(struct child *ptr); @@ -55,23 +54,6 @@ void initialise_configuration(); -/* - * most of the preforking code was taken from the tinyproxy project - */ - -signal_func *set_signal_handler(int signo, signal_func * func){ - struct sigaction act, oact; - - act.sa_handler = func; - sigemptyset (&act.sa_mask); - act.sa_flags = 0; - - if(sigaction(signo, &act, &oact) < 0) return SIG_ERR; - - return oact.sa_handler; -} - - static void takesig(int sig){ int i, status; pid_t pid; @@ -118,12 +100,6 @@ static void child_sighup_handler(int sig){ } -void *get_in_addr(struct sockaddr *sa){ - if(sa->sa_family == AF_INET) return &(((struct sockaddr_in*)sa)->sin_addr); - return &(((struct sockaddr_in6*)sa)->sin6_addr); -} - - static void child_main(struct child *ptr){ int new_sd; char s[INET6_ADDRSTRLEN]; @@ -368,7 +344,7 @@ void initialise_configuration(){ int main(int argc, char **argv){ int i, rc, yes=1, daemonise=0; - char port_string[6]; + char port_string[8]; struct addrinfo hints, *res; diff --git a/src/piler.h b/src/piler.h index 6e1f752f..deac27f0 100644 --- a/src/piler.h +++ b/src/piler.h @@ -35,6 +35,7 @@ void digest_file(char *filename, char *digest); void digest_string(char *s, char *digest); int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg); +int handle_pilerget_request(int new_sd, struct __data *data, struct __config *cfg); void remove_stripped_attachments(struct _state *state); int process_message(struct session_data *sdata, struct _state *state, struct __data *data, struct __config *cfg); @@ -50,6 +51,7 @@ void check_and_create_directories(struct __config *cfg, uid_t uid, gid_t gid); void update_counters(struct session_data *sdata, struct __data *data, struct __counters *counters, struct __config *cfg); int retrieve_email_from_archive(struct session_data *sdata, struct __data *data, FILE *dest, struct __config *cfg); +int file_from_archive_to_network(char *filename, int sd, struct __data *data, struct __config *cfg); int prepare_a_mysql_statement(struct session_data *sdata, MYSQL_STMT **stmt, char *s); diff --git a/src/pilergetd.c b/src/pilergetd.c new file mode 100644 index 00000000..768e33d1 --- /dev/null +++ b/src/pilergetd.c @@ -0,0 +1,399 @@ +/* + * pilergetd.c, SJ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern char *optarg; +extern int optind; + +int sd; +int quit = 0; +int received_sighup = 0; +char *configfile = CONFIG_FILE; +struct __config cfg; +struct __data data; +struct passwd *pwd; + +struct child children[MAXCHILDREN]; + + +static void takesig(int sig); +static void child_sighup_handler(int sig); +static void child_main(struct child *ptr); +static pid_t child_make(struct child *ptr); +int search_slot_by_pid(pid_t pid); +void kill_children(int sig); +void p_clean_exit(); +void fatal(char *s); +void initialise_configuration(); + + + + +static void takesig(int sig){ + int i, status; + pid_t pid; + + switch(sig){ + case SIGHUP: + initialise_configuration(); + if(read_key(&cfg)) fatal(ERR_READING_KEY); + kill_children(SIGHUP); + break; + + case SIGTERM: + case SIGKILL: + quit = 1; + p_clean_exit(); + break; + + case SIGCHLD: + while((pid = waitpid (-1, &status, WNOHANG)) > 0){ + + if(quit == 0){ + i = search_slot_by_pid(pid); + if(i >= 0){ + children[i].status = READY; + children[i].pid = child_make(&children[i]); + } + else syslog(LOG_PRIORITY, "error: couldn't find slot for pid %d", pid); + + } + } + break; + } + + return; +} + + +static void child_sighup_handler(int sig){ + if(sig == SIGHUP){ + received_sighup = 1; + } +} + + +static void child_main(struct child *ptr){ + int new_sd; + char s[INET6_ADDRSTRLEN]; + struct sockaddr_storage client_addr; + socklen_t addr_size; + + ptr->messages = 0; + + if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "child (pid: %d) started main()", getpid()); + + while(1){ + if(received_sighup == 1){ + if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "child (pid: %d) caught HUP signal", getpid()); + break; + } + + ptr->status = READY; + + addr_size = sizeof(client_addr); + new_sd = accept(sd, (struct sockaddr *)&client_addr, &addr_size); + + if(new_sd == -1) continue; + + ptr->status = BUSY; + + inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), s, sizeof(s)); + + syslog(LOG_PRIORITY, "connection from %s", s); + + + sig_block(SIGHUP); + ptr->messages += handle_pilerget_request(new_sd, &data, &cfg); + sig_unblock(SIGHUP); + + close(new_sd); + + if(cfg.max_requests_per_child > 0 && ptr->messages >= cfg.max_requests_per_child){ + if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "child (pid: %d) served enough: %d", getpid(), ptr->messages); + break; + } + + } + + ptr->status = UNDEF; + +#ifdef HAVE_MEMCACHED + memcached_shutdown(&(data.memc)); +#endif + + if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "child decides to exit (pid: %d)", getpid()); + + exit(0); +} + + +static pid_t child_make(struct child *ptr){ + pid_t pid; + + if((pid = fork()) > 0) return pid; + + if(pid == -1) return -1; + + if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "forked a child (pid: %d)", getpid()); + + /* reset signals */ + + set_signal_handler(SIGCHLD, SIG_DFL); + set_signal_handler(SIGTERM, SIG_DFL); + set_signal_handler(SIGHUP, child_sighup_handler); + + child_main(ptr); + + return -1; +} + + + +int child_pool_create(){ + int i; + + for(i=0; i 1){ + if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "sending signal to child (pid: %d)", children[i].pid); + kill(children[i].pid, sig); + } + } +} + + +void p_clean_exit(){ + if(sd != -1) close(sd); + + kill_children(SIGTERM); + + syslog(LOG_PRIORITY, "%s has been terminated", PILERGETD_PROGNAME); + + unlink(cfg.pilergetd_pidfile); + + if(data.ctx){ + SSL_CTX_free(data.ctx); + ERR_free_strings(); + } + + exit(1); +} + + +void fatal(char *s){ + syslog(LOG_PRIORITY, "%s\n", s); + p_clean_exit(); +} + + +int init_ssl(){ + + SSL_library_init(); + SSL_load_error_strings(); + + data.ctx = SSL_CTX_new(SSLv23_server_method()); + + if(data.ctx == NULL){ syslog(LOG_PRIORITY, "SSL_CTX_new() failed"); return ERR; } + + if(SSL_CTX_set_cipher_list(data.ctx, cfg.cipher_list) == 0){ syslog(LOG_PRIORITY, "failed to set cipher list: '%s'", cfg.cipher_list); return ERR; } + + if(SSL_CTX_use_PrivateKey_file(data.ctx, cfg.pemfile, SSL_FILETYPE_PEM) != 1){ syslog(LOG_PRIORITY, "cannot load private key from %s", cfg.pemfile); return ERR; } + + if(SSL_CTX_use_certificate_file(data.ctx, cfg.pemfile, SSL_FILETYPE_PEM) != 1){ syslog(LOG_PRIORITY, "cannot load certificate from %s", cfg.pemfile); return ERR; } + + return OK; +} + + +void initialise_configuration(){ + + cfg = read_config(configfile); + + if(cfg.number_of_worker_processes < 5) cfg.number_of_worker_processes = 5; + if(cfg.number_of_worker_processes > MAXCHILDREN) cfg.number_of_worker_processes = MAXCHILDREN; + + if(strlen(cfg.username) > 1){ + pwd = getpwnam(cfg.username); + if(!pwd) fatal(ERR_NON_EXISTENT_USER); + } + + + if(getuid() == 0 && pwd){ + check_and_create_directories(&cfg, pwd->pw_uid, pwd->pw_gid); + } + + + if(chdir(cfg.workdir)){ + syslog(LOG_PRIORITY, "workdir: *%s*", cfg.workdir); + fatal(ERR_CHDIR); + } + + setlocale(LC_MESSAGES, cfg.locale); + setlocale(LC_CTYPE, cfg.locale); + + syslog(LOG_PRIORITY, "reloaded config: %s", configfile); + +#ifdef HAVE_MEMCACHED + memcached_init(&(data.memc), cfg.memcached_servers, 11211); +#endif +} + + +int main(int argc, char **argv){ + int i, rc, yes=1, daemonise=0; + char port_string[8]; + struct addrinfo hints, *res; + + + while((i = getopt(argc, argv, "c:dvVh")) > 0){ + switch(i){ + + case 'c' : + configfile = optarg; + break; + + case 'd' : + daemonise = 1; + break; + + case 'v' : + case 'V' : + printf("%s %s, build %d, Janos SUTO \n\n%s\n\n", PILERGETD_PROGNAME, VERSION, get_build(), CONFIGURE_PARAMS); + return 0; + + case 'h' : + default : + __fatal("usage: ..."); + } + } + + (void) openlog(PILERGETD_PROGNAME, LOG_PID, LOG_MAIL); + + data.ctx = NULL; + data.ssl = NULL; + + initialise_configuration(); + + if(init_ssl() == ERR) fatal("cannot init ssl"); + + set_signal_handler (SIGPIPE, SIG_IGN); + + + if(read_key(&cfg)) fatal(ERR_READING_KEY); + + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + snprintf(port_string, sizeof(port_string)-1, "%d", cfg.pilergetd_listen_port); + + if((rc = getaddrinfo(cfg.pilergetd_listen_addr, port_string, &hints, &res)) != 0){ + fprintf(stderr, "getaddrinfo for '%s': %s\n", cfg.pilergetd_listen_addr, gai_strerror(rc)); + return 1; + } + + + if((sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) + fatal(ERR_OPEN_SOCKET); + + if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) + fatal(ERR_SET_SOCK_OPT); + + if(bind(sd, res->ai_addr, res->ai_addrlen) == -1) + fatal(ERR_BIND_TO_PORT); + + if(listen(sd, cfg.backlog) == -1) + fatal(ERR_LISTEN); + + + freeaddrinfo(res); + + + if(drop_privileges(pwd)) fatal(ERR_SETUID); + + + syslog(LOG_PRIORITY, "%s %s, build %d starting", PILERGETD_PROGNAME, VERSION, get_build()); + + +#if HAVE_DAEMON == 1 + if(daemonise == 1) i = daemon(1, 0); +#endif + + write_pid_file(cfg.pilergetd_pidfile); + + + child_pool_create(); + + set_signal_handler(SIGCHLD, takesig); + set_signal_handler(SIGTERM, takesig); + set_signal_handler(SIGKILL, takesig); + set_signal_handler(SIGHUP, takesig); + + + for(;;){ sleep(1); } + + p_clean_exit(); + + return 0; +} + diff --git a/src/pop3.c b/src/pop3.c index fc51f5a3..2deecde4 100644 --- a/src/pop3.c +++ b/src/pop3.c @@ -75,13 +75,13 @@ int connect_to_pop3_server(int sd, char *username, char *password, int port, str snprintf(buf, sizeof(buf)-1, "USER %s\r\n", username); - write1(sd, buf, use_ssl, data->ssl); + write1(sd, buf, strlen(buf), use_ssl, data->ssl); n = recvtimeoutssl(sd, buf, sizeof(buf), 10, use_ssl, data->ssl); snprintf(buf, sizeof(buf)-1, "PASS %s\r\n", password); - write1(sd, buf, use_ssl, data->ssl); + write1(sd, buf, strlen(buf), use_ssl, data->ssl); n = recvtimeoutssl(sd, buf, sizeof(buf), 10, use_ssl, data->ssl); if(strncmp(buf, "+OK", 3) == 0) return OK; @@ -96,7 +96,7 @@ int process_pop3_emails(int sd, struct session_data *sdata, struct __data *data, char aggrbuf[3*MAXBUFSIZE]; snprintf(buf, sizeof(buf)-1, "STAT\r\n"); - n = write1(sd, buf, use_ssl, data->ssl); + n = write1(sd, buf, strlen(buf), use_ssl, data->ssl); n = recvtimeoutssl(sd, buf, sizeof(buf), 10, use_ssl, data->ssl); @@ -130,7 +130,7 @@ int process_pop3_emails(int sd, struct session_data *sdata, struct __data *data, return rc; } - n = write1(sd, buf, use_ssl, data->ssl); + n = write1(sd, buf, strlen(buf), use_ssl, data->ssl); readlen = 0; pos = 0; @@ -195,7 +195,7 @@ int process_pop3_emails(int sd, struct session_data *sdata, struct __data *data, snprintf(buf, sizeof(buf)-1, "QUIT\r\n"); - n = write1(sd, buf, use_ssl, data->ssl); + n = write1(sd, buf, strlen(buf), use_ssl, data->ssl); printf("\n"); diff --git a/src/retr.c b/src/retr.c new file mode 100644 index 00000000..e375430c --- /dev/null +++ b/src/retr.c @@ -0,0 +1,251 @@ +/* + * retr.c, SJ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +int stat_file(struct session_data *sdata, char *f, char **buf, int buflen, struct __config *cfg){ + struct stat st; + + snprintf(*buf, buflen, "%s/%02x/%c%c%c/%c%c/%c%c/%s", cfg->queuedir, cfg->server_id, f[8], f[9], f[10], f[RND_STR_LEN-4], f[RND_STR_LEN-3], f[RND_STR_LEN-2], f[RND_STR_LEN-1], f); + if(!stat(*buf, &st)) return st.st_size; + + snprintf(*buf, TINYBUFSIZE-1, "%s/%02x/%c%c/%c%c/%c%c/%s", cfg->queuedir, cfg->server_id, f[RND_STR_LEN-6], f[RND_STR_LEN-5], f[RND_STR_LEN-4], f[RND_STR_LEN-3], f[RND_STR_LEN-2], f[RND_STR_LEN-1], f); + if(!stat(*buf, &st)) return st.st_size; + + return 0; +} + + +int stat_message(struct session_data *sdata, struct __data *data, char **buf, int buflen, struct __config *cfg){ + int i, attachments, len=0; + struct ptr_array ptr_arr[MAX_ATTACHMENTS]; + char puf[TINYBUFSIZE]; + + if(strlen(sdata->ttmpfile) != RND_STR_LEN){ + return ERR; + } + + snprintf(*buf, buflen-2, "%s.m", sdata->ttmpfile); + len = strlen(*buf); + + attachments = query_attachments(sdata, data, &ptr_arr[0], cfg); + + if(attachments == -1){ + return ERR; + } + + for(i=1; i<=attachments; i++){ + if(len < buflen){ + snprintf(puf, sizeof(puf)-1, " %s.a%d", ptr_arr[i].piler_id, ptr_arr[i].attachment_id); + strncat(*buf, puf, buflen); + len += strlen(puf); + } + } + + strncat(*buf, "\r\n", buflen-2); + + return OK; +} + + +int handle_pilerget_request(int new_sd, struct __data *data, struct __config *cfg){ + int len, n, ssl_ok=0, n_files=0; + char *q, buf[MAXBUFSIZE], puf[MAXBUFSIZE], muf[TINYBUFSIZE], resp[MAXBUFSIZE]; + char ssl_error[SMALLBUFSIZE]; + struct session_data sdata; + int db_conn=0; + int rc; + struct __counters counters; + + struct timezone tz; + struct timeval tv1, tv2; + +#ifdef HAVE_LIBWRAP + struct request_info req; + + request_init(&req, RQ_DAEMON, PILERGETD_PROGNAME, RQ_FILE, new_sd, 0); + fromhost(&req); + if(!hosts_access(&req)){ + send(new_sd, SMTP_RESP_550_ERR_YOU_ARE_BANNED_BY_LOCAL_POLICY, strlen(SMTP_RESP_550_ERR_YOU_ARE_BANNED_BY_LOCAL_POLICY), 0); + syslog(LOG_PRIORITY, "denied connection from %s by tcp_wrappers", eval_client(&req)); + return 0; + } +#endif + + + init_session_data(&sdata, cfg); + sdata.tls = 0; + + bzero(&counters, sizeof(counters)); + + + /* open database connection */ + + db_conn = 0; + +#ifdef NEED_MYSQL + rc = 1; + mysql_init(&(sdata.mysql)); + mysql_options(&(sdata.mysql), MYSQL_OPT_CONNECT_TIMEOUT, (const char*)&cfg->mysql_connect_timeout); + mysql_options(&(sdata.mysql), MYSQL_OPT_RECONNECT, (const char*)&rc); + + if(mysql_real_connect(&(sdata.mysql), cfg->mysqlhost, cfg->mysqluser, cfg->mysqlpwd, cfg->mysqldb, cfg->mysqlport, cfg->mysqlsocket, 0)){ + db_conn = 1; + mysql_real_query(&(sdata.mysql), "SET NAMES utf8", strlen("SET NAMES utf8")); + mysql_real_query(&(sdata.mysql), "SET CHARACTER SET utf8", strlen("SET CHARACTER SET utf8")); + } + else + syslog(LOG_PRIORITY, "%s", ERR_MYSQL_CONNECT); +#endif + + if(db_conn == 1 && create_prepared_statements(&sdata, data) == ERR){ + close_prepared_statements(data); + mysql_close(&(sdata.mysql)); + db_conn = 0; + } + + + if(db_conn == 0){ + send(new_sd, SMTP_RESP_421_ERR_TMP, strlen(SMTP_RESP_421_ERR_TMP), 0); + syslog(LOG_PRIORITY, "cannot make prepared statement"); + return 0; + } + + + gettimeofday(&tv1, &tz); + + + if(data->ctx){ + data->ssl = SSL_new(data->ctx); + if(data->ssl){ + if(SSL_set_fd(data->ssl, new_sd) == 1){ + ssl_ok = 1; + } else syslog(LOG_PRIORITY, "SSL_set_fd() failed"); + } else syslog(LOG_PRIORITY, "SSL_new() failed"); + } else syslog(LOG_PRIORITY, "SSL ctx is null!"); + + + if(ssl_ok == 0){ + send(new_sd, SMTP_RESP_421_ERR_TMP, strlen(SMTP_RESP_421_ERR_TMP), 0); + return 0; + } + + + rc = SSL_accept(data->ssl); + + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "SSL_accept() finished"); + + if(rc == 1){ + sdata.tls = 1; + } + else { + ERR_error_string_n(ERR_get_error(), ssl_error, SMALLBUFSIZE); + syslog(LOG_PRIORITY, "SSL_accept() failed, rc=%d, errorcode: %d, error text: %s\n", rc, SSL_get_error(data->ssl, rc), ssl_error); + goto QUITTING; + } + + + snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_220_BANNER, cfg->hostid); + + write1(new_sd, buf, strlen(buf), sdata.tls, data->ssl); + + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "sent: %s", buf); + + while((n = recvtimeoutssl(new_sd, puf, MAXBUFSIZE, TIMEOUT, sdata.tls, data->ssl)) > 0){ + + if(strncasecmp(puf, "MESSAGE ", strlen("MESSAGE ")) == 0){ + trimBuffer(puf); + q = &resp[0]; + + memset(resp, 0, sizeof(resp)); + + snprintf(sdata.ttmpfile, sizeof(sdata.ttmpfile)-1, "%s", &puf[8]); + stat_message(&sdata, data, &q, sizeof(resp)-2, cfg); + + write1(new_sd, resp, strlen(resp), sdata.tls, data->ssl); + + continue; + } + + + if(strncasecmp(puf, "STAT ", strlen("STAT ")) == 0){ + trimBuffer(puf); + q = &muf[0]; + + len = stat_file(&sdata, &puf[5], &q, sizeof(muf)-2, cfg); + + snprintf(resp, sizeof(resp)-1, "SIZE %s %d\r\n", &puf[5], len); + + write1(new_sd, resp, strlen(resp), sdata.tls, data->ssl); + + continue; + } + + + if(strncasecmp(puf, "RETR ", strlen("RETR ")) == 0){ + + trimBuffer(puf); + q = &muf[0]; + + if(strlen(&puf[5]) >= RND_STR_LEN){ + len = stat_file(&sdata, &puf[5], &q, sizeof(muf)-2, cfg); + file_from_archive_to_network(muf, new_sd, data, cfg); + n_files++; + } + else { + snprintf(resp, sizeof(resp)-1, "ERR\r\n"); + write1(new_sd, resp, strlen(resp), sdata.tls, data->ssl); + } + + continue; + } + + + if(strncasecmp(puf, SMTP_CMD_QUIT, strlen(SMTP_CMD_QUIT)) == 0){ + snprintf(resp, sizeof(resp)-1, SMTP_RESP_221_GOODBYE, cfg->hostid); + write1(new_sd, resp, strlen(resp), sdata.tls, data->ssl); + + gettimeofday(&tv2, &tz); + + break; + } + + snprintf(resp, sizeof(resp)-1, "ERR\r\n"); + write1(new_sd, resp, strlen(resp), sdata.tls, data->ssl); + + } + + + +QUITTING: + +#ifdef NEED_MYSQL + close_prepared_statements(data); + mysql_close(&(sdata.mysql)); +#endif + + SSL_shutdown(data->ssl); + SSL_free(data->ssl); + + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "served %d files", n_files); + + return 1; +} + + diff --git a/src/session.c b/src/session.c index 23212831..1cc59908 100644 --- a/src/session.c +++ b/src/session.c @@ -154,8 +154,7 @@ int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){ for(i=0; issl); + write1(new_sd, SMTP_RESP_421_ERR_WRITE_FAILED, strlen(SMTP_RESP_421_ERR_WRITE_FAILED), sdata.tls, data->ssl); #ifdef HAVE_LMTP } @@ -250,8 +249,7 @@ int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){ if(inj == ERR) snprintf(sdata.acceptbuf, SMALLBUFSIZE-1, "451 %s <%s>\r\n", sdata.ttmpfile, rctptoemail); - //send(new_sd, sdata.acceptbuf, strlen(sdata.acceptbuf), 0); - write1(new_sd, sdata.acceptbuf, sdata.tls, data->ssl); + write1(new_sd, sdata.acceptbuf, strlen(sdata.acceptbuf), sdata.tls, data->ssl); if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, sdata.acceptbuf); @@ -506,8 +504,7 @@ AFTER_PERIOD: /* now we can send our buffered response */ if(strlen(resp) > 0){ - //send(new_sd, resp, strlen(resp), 0); - write1(new_sd, resp, sdata.tls, data->ssl); + write1(new_sd, resp, strlen(resp), sdata.tls, data->ssl); if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, resp); memset(resp, 0, MAXBUFSIZE); @@ -547,8 +544,7 @@ AFTER_PERIOD: if(state < SMTP_STATE_QUIT && inj == ERR){ snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_421_ERR, cfg->hostid); - //send(new_sd, buf, strlen(buf), 0); - write1(new_sd, buf, sdata.tls, data->ssl); + write1(new_sd, buf, strlen(buf), sdata.tls, data->ssl); if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, buf); diff --git a/src/sig.c b/src/sig.c index 2cf2b3fa..6d28b0a7 100644 --- a/src/sig.c +++ b/src/sig.c @@ -5,6 +5,8 @@ #include #include #include +#include + void sig_block(int sig){ sigset_t ss; @@ -43,3 +45,15 @@ int wait_nohang(int *wstat){ return waitpid(-1, wstat, WNOHANG); } +signal_func *set_signal_handler(int signo, signal_func * func){ + struct sigaction act, oact; + + act.sa_handler = func; + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + + if(sigaction(signo, &act, &oact) < 0) return SIG_ERR; + + return oact.sa_handler; +} + diff --git a/src/sig.h b/src/sig.h index b1302dc6..86e1e3a8 100644 --- a/src/sig.h +++ b/src/sig.h @@ -11,5 +11,6 @@ void sig_catch(int sig, void (*f)()); void sig_uncatch(int sig); void sig_pause(); int wait_nohang(int *wstat); +signal_func *set_signal_handler(int signo, signal_func * func); #endif