mirror of
https://bitbucket.org/jsuto/piler.git
synced 2024-11-07 17:41:59 +01:00
Introduced smtp acl
Signed-off-by: Janos SUTO <sj@acts.hu>
This commit is contained in:
parent
48ec9b5c4a
commit
f8a32dd025
@ -2,6 +2,7 @@
|
||||
-------
|
||||
|
||||
- Added security header feature
|
||||
- Introduced the smtp acl list, and obsoleted the tcp_wrappers check
|
||||
|
||||
|
||||
1.3.9:
|
||||
|
19
configure
vendored
19
configure
vendored
@ -4714,23 +4714,6 @@ else
|
||||
echo "zip library: no"
|
||||
fi
|
||||
|
||||
if test "$have_tcpwrappers" = "yes"; then
|
||||
echo "tcpwrappers support: yes"
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define HAVE_LIBWRAP 1
|
||||
_ACEOF
|
||||
|
||||
|
||||
if test "$os" = "FreeBSD"; then
|
||||
antispam_libs="$antispam_libs -lwrap"
|
||||
else
|
||||
antispam_libs="$antispam_libs -lwrap -lnsl"
|
||||
fi
|
||||
else
|
||||
echo "tcpwrappers support: no"
|
||||
fi
|
||||
|
||||
|
||||
echo
|
||||
|
||||
@ -4860,7 +4843,7 @@ fi
|
||||
|
||||
CFLAGS="$static -std=c99 -O2 -fPIC -Wall -Wextra $extra_cflags -Wuninitialized -Wno-format-truncation -g"
|
||||
LIBS="$antispam_libs $sunos_libs "
|
||||
OBJS="dirs.o misc.o counters.o cfg.o sig.o decoder.o hash.o parser.o parser_utils.o rules.o smtp.o session.o bdat.o message.o attachment.o digest.o store.o archive.o tai.o import.o import_pilerexport.o import_maildir.o import_mailbox.o import_pop3.o import_imap.o imap.o pop3.o extract.o mydomains.o tokenizer.o $objs"
|
||||
OBJS="dirs.o misc.o counters.o cfg.o sig.o decoder.o hash.o parser.o parser_utils.o rules.o smtp.o session.o bdat.o message.o attachment.o digest.o store.o archive.o tai.o import.o import_pilerexport.o import_maildir.o import_mailbox.o import_pop3.o import_imap.o imap.o pop3.o extract.o mydomains.o tokenizer.o screen.o $objs"
|
||||
|
||||
ac_config_files="$ac_config_files Makefile src/Makefile etc/Makefile util/Makefile init.d/Makefile systemd/Makefile unit_tests/Makefile webui/Makefile contrib/imap/Makefile"
|
||||
|
||||
|
15
configure.in
15
configure.in
@ -433,19 +433,6 @@ else
|
||||
echo "zip library: no"
|
||||
fi
|
||||
|
||||
if test "$have_tcpwrappers" = "yes"; then
|
||||
echo "tcpwrappers support: yes"
|
||||
AC_DEFINE_UNQUOTED(HAVE_LIBWRAP, 1, [tcpwrappers support])
|
||||
|
||||
if test "$os" = "FreeBSD"; then
|
||||
antispam_libs="$antispam_libs -lwrap"
|
||||
else
|
||||
antispam_libs="$antispam_libs -lwrap -lnsl"
|
||||
fi
|
||||
else
|
||||
echo "tcpwrappers support: no"
|
||||
fi
|
||||
|
||||
|
||||
echo
|
||||
|
||||
@ -543,7 +530,7 @@ fi
|
||||
|
||||
CFLAGS="$static -std=c99 -O2 -fPIC -Wall -Wextra $extra_cflags -Wuninitialized -Wno-format-truncation -g"
|
||||
LIBS="$antispam_libs $sunos_libs "
|
||||
OBJS="dirs.o misc.o counters.o cfg.o sig.o decoder.o hash.o parser.o parser_utils.o rules.o smtp.o session.o bdat.o message.o attachment.o digest.o store.o archive.o tai.o import.o import_pilerexport.o import_maildir.o import_mailbox.o import_pop3.o import_imap.o imap.o pop3.o extract.o mydomains.o tokenizer.o $objs"
|
||||
OBJS="dirs.o misc.o counters.o cfg.o sig.o decoder.o hash.o parser.o parser_utils.o rules.o smtp.o session.o bdat.o message.o attachment.o digest.o store.o archive.o tai.o import.o import_pilerexport.o import_maildir.o import_mailbox.o import_pop3.o import_imap.o imap.o pop3.o extract.o mydomains.o tokenizer.o screen.o $objs"
|
||||
|
||||
AC_CONFIG_FILES([Makefile src/Makefile etc/Makefile util/Makefile init.d/Makefile systemd/Makefile unit_tests/Makefile webui/Makefile contrib/imap/Makefile])
|
||||
AC_OUTPUT
|
||||
|
@ -230,3 +230,13 @@ mmap_dedup_test=0
|
||||
; mechanism against unwanted email in the archive if limiting smtp
|
||||
; clients via an IP-address list is not feasible.
|
||||
security_header=
|
||||
|
||||
; whether to enable (1) or not (0) an smtp access list similar to
|
||||
; postfix's postscreen. If so, then create a text file %sysconfdir%/piler/smtp.acl
|
||||
; An example for /usr/local/etc/piler/smtp.acl:
|
||||
;
|
||||
; 1.2.3.4/32 permit
|
||||
; 10.0.1.0/24 reject
|
||||
; 172.16.0.0/16 permit
|
||||
;
|
||||
smtp_access_list=0
|
||||
|
@ -26,8 +26,6 @@
|
||||
#undef HAVE_TNEF
|
||||
#undef HAVE_ZIP
|
||||
|
||||
#undef HAVE_LIBWRAP
|
||||
|
||||
#undef HAVE_TWEAK_SENT_TIME
|
||||
|
||||
#undef HAVE_SUPPORT_FOR_COMPAT_STORAGE_LAYOUT
|
||||
|
@ -48,7 +48,7 @@ libpiler.a: $(OBJS) $(SQL_OBJS)
|
||||
ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION)
|
||||
|
||||
piler-smtp: piler-smtp.c libpiler.a
|
||||
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $< cfg.o misc.o tai.o smtp.o session.o dirs.o sig.o bdat.o $(LIBS) $(LIBDIR)
|
||||
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $< cfg.o misc.o tai.o smtp.o session.o dirs.o sig.o bdat.o screen.o $(LIBS) $(LIBDIR)
|
||||
|
||||
pilerget: pilerget.c libpiler.a
|
||||
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $< -lpiler $(LIBS) $(LIBDIR)
|
||||
|
@ -86,6 +86,7 @@ struct _parse_rule config_parse_rules[] =
|
||||
{ "queuedir", "string", (void*) string_parser, offsetof(struct config, queuedir), QUEUE_DIR, MAXVAL-1},
|
||||
{ "security_header", "string", (void*) string_parser, offsetof(struct config, security_header), "", MAXVAL-1},
|
||||
{ "server_id", "integer", (void*) int_parser, offsetof(struct config, server_id), "0", sizeof(int)},
|
||||
{ "smtp_access_list", "integer", (void*) int_parser, offsetof(struct config, smtp_access_list), "0", sizeof(int)},
|
||||
{ "smtp_timeout", "integer", (void*) int_parser, offsetof(struct config, smtp_timeout), "60", sizeof(int)},
|
||||
{ "spam_header_line", "string", (void*) string_parser, offsetof(struct config, spam_header_line), "", MAXVAL-1},
|
||||
{ "syslog_recipients", "integer", (void*) int_parser, offsetof(struct config, syslog_recipients), "0", sizeof(int)},
|
||||
|
@ -99,6 +99,8 @@ struct config {
|
||||
int enable_folders;
|
||||
|
||||
int debug;
|
||||
|
||||
int smtp_access_list;
|
||||
};
|
||||
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#define HOSTID "mailarchiver"
|
||||
|
||||
#define CONFIG_FILE CONFDIR "/piler/piler.conf"
|
||||
#define SMTP_ACL_FILE CONFDIR "/piler/smtp.acl"
|
||||
#define WORK_DIR DATADIR "/piler/tmp"
|
||||
#define QUEUE_DIR DATADIR "/piler/store"
|
||||
#define ERROR_DIR DATADIR "/piler/error"
|
||||
|
12
src/defs.h
12
src/defs.h
@ -13,9 +13,6 @@
|
||||
#include <tre/tre.h>
|
||||
#include <tre/regex.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIBWRAP
|
||||
#include <tcpd.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/ssl.h>
|
||||
@ -98,6 +95,15 @@ struct node {
|
||||
};
|
||||
|
||||
|
||||
struct smtp_acl {
|
||||
char network_str[BUFLEN];
|
||||
in_addr_t low, high;
|
||||
int prefix;
|
||||
int rejected;
|
||||
struct smtp_acl *r;
|
||||
};
|
||||
|
||||
|
||||
struct net {
|
||||
int socket;
|
||||
int use_ssl;
|
||||
|
@ -38,6 +38,7 @@ char *configfile = CONFIG_FILE;
|
||||
struct config cfg;
|
||||
struct passwd *pwd;
|
||||
struct smtp_session *session, **sessions=NULL;
|
||||
struct smtp_acl *smtp_acl[MAXHASH];
|
||||
|
||||
|
||||
void usage(){
|
||||
@ -66,6 +67,8 @@ void p_clean_exit(int sig){
|
||||
|
||||
if(events) free(events);
|
||||
|
||||
clear_smtp_acl(smtp_acl);
|
||||
|
||||
syslog(LOG_PRIORITY, "%s has been terminated", PROGNAME);
|
||||
|
||||
ERR_free_strings();
|
||||
@ -120,6 +123,8 @@ void initialise_configuration(){
|
||||
setlocale(LC_MESSAGES, cfg.locale);
|
||||
setlocale(LC_CTYPE, cfg.locale);
|
||||
|
||||
load_smtp_acl(smtp_acl);
|
||||
|
||||
syslog(LOG_PRIORITY, "reloaded config: %s", configfile);
|
||||
}
|
||||
|
||||
@ -270,7 +275,7 @@ int main(int argc, char **argv){
|
||||
break;
|
||||
}
|
||||
|
||||
start_new_session(sessions, client_sockfd, &num_connections, &cfg);
|
||||
start_new_session(sessions, client_sockfd, &num_connections, smtp_acl, hbuf, &cfg);
|
||||
}
|
||||
|
||||
continue;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <sig.h>
|
||||
#include <av.h>
|
||||
#include <rules.h>
|
||||
#include <screen.h>
|
||||
#include <sql.h>
|
||||
#include <import.h>
|
||||
#include <smtp.h>
|
||||
@ -68,7 +69,7 @@ int retrieve_file_from_archive(char *filename, int mode, char **buffer, FILE *de
|
||||
void load_mydomains(struct session_data *sdata, struct data *data, struct config *cfg);
|
||||
int is_email_address_on_my_domains(char *email, struct data *data);
|
||||
|
||||
int start_new_session(struct smtp_session **sessions, int socket, int *num_connections, struct config *cfg);
|
||||
int start_new_session(struct smtp_session **sessions, int socket, int *num_connections, struct smtp_acl *smtp_acl[], char *client_addr, struct config *cfg);
|
||||
void tear_down_session(struct smtp_session **sessions, int slot, int *num_connections);
|
||||
struct smtp_session *get_session_by_socket(struct smtp_session **sessions, int max_connections, int socket);
|
||||
void write_envelope_addresses(struct smtp_session *session, struct config *cfg);
|
||||
|
237
src/screen.c
Normal file
237
src/screen.c
Normal file
@ -0,0 +1,237 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <netdb.h>
|
||||
#include <piler.h>
|
||||
|
||||
|
||||
void init_smtp_acl(struct smtp_acl *smtp_acl[]){
|
||||
smtp_acl[0] = NULL;
|
||||
}
|
||||
|
||||
|
||||
void clear_smtp_acl(struct smtp_acl *smtp_acl[]){
|
||||
struct smtp_acl *q;
|
||||
|
||||
q = smtp_acl[0];
|
||||
|
||||
while(q){
|
||||
struct smtp_acl *p = q;
|
||||
q = q->r;
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
smtp_acl[0] = NULL;
|
||||
}
|
||||
|
||||
|
||||
int add_smtp_acl(struct smtp_acl *smtp_acl[], char *network_str, struct smtp_acl *acl){
|
||||
struct smtp_acl *q, *p=NULL, *node;
|
||||
|
||||
if((node = malloc(sizeof(struct smtp_acl))) == NULL) return 0;
|
||||
|
||||
memset(node, 0, sizeof(struct smtp_acl));
|
||||
|
||||
node->low = acl->low;
|
||||
node->high = acl->high;
|
||||
node->prefix = acl->prefix;
|
||||
node->rejected = acl->rejected;
|
||||
snprintf(node->network_str, sizeof(node->network_str)-1, "%s", network_str);
|
||||
|
||||
node->r = NULL;
|
||||
|
||||
q = smtp_acl[0];
|
||||
|
||||
while(q){
|
||||
p = q;
|
||||
q = q->r;
|
||||
}
|
||||
|
||||
if(!p){
|
||||
smtp_acl[0] = node;
|
||||
} else {
|
||||
p->r = node;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int is_valid_line(char *line){
|
||||
// Skip comments
|
||||
if(line[0] == ';' || line[0] == '#') return 0;
|
||||
|
||||
trimBuffer(line);
|
||||
|
||||
// Skip empty line
|
||||
if(line[0] == 0) return 0;
|
||||
|
||||
// Currently we support ipv4 stuff only, ie. valid characters are: 0-9./
|
||||
// and a line should look like "1.2.3.4/24 permit" or similar (without quotes)
|
||||
|
||||
if(!strchr(line, '.') || !strchr(line, '/') || (!strchr(line, ' ') && !strchr(line, '\t')) ){
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ascii values:
|
||||
// 46: .
|
||||
// 47: /
|
||||
// 48-57: 0-9
|
||||
// 65-90: A-Z
|
||||
// 97-122: a-z
|
||||
for(; *line; line++){
|
||||
if(isalnum(*line) == 0 && isblank(*line) == 0 && *line != 46 && *line != 47) return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int a_to_hl(char *ipstr, in_addr_t *addr){
|
||||
struct in_addr in;
|
||||
|
||||
if(inet_aton(ipstr, &in) == 1){
|
||||
*addr = ntohl(in.s_addr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
in_addr_t netmask(int prefix){
|
||||
if(prefix == 0)
|
||||
return( ~((in_addr_t) -1) );
|
||||
else
|
||||
return ~((1 << (32 - prefix)) - 1);
|
||||
}
|
||||
|
||||
|
||||
in_addr_t network(in_addr_t addr, int prefix){
|
||||
return addr & netmask(prefix);
|
||||
}
|
||||
|
||||
|
||||
in_addr_t broadcast(in_addr_t addr, int prefix){
|
||||
return addr | ~netmask(prefix);
|
||||
}
|
||||
|
||||
|
||||
int str_to_net_range(char *network_addr_prefix, struct smtp_acl *smtp_acl){
|
||||
in_addr_t net = 0;
|
||||
int prefix = 0;
|
||||
|
||||
smtp_acl->low = 0;
|
||||
smtp_acl->high = 0;
|
||||
|
||||
// By default we permit unless you specify "reject" (without quotes)
|
||||
// To be on the safer side we permit even if you misspell the word "reject"
|
||||
|
||||
smtp_acl->rejected = 0;
|
||||
|
||||
if(strcasestr(network_addr_prefix, "reject")){
|
||||
smtp_acl->rejected = 1;
|
||||
}
|
||||
|
||||
char *p = strchr(network_addr_prefix, '/');
|
||||
if(!p) return 0;
|
||||
|
||||
if(strlen(network_addr_prefix) > sizeof(smtp_acl->network_str)){
|
||||
syslog(LOG_PRIORITY, "line *%s* is longer than %ld bytes, discarded", network_addr_prefix, sizeof(smtp_acl->network_str));
|
||||
return 0;
|
||||
}
|
||||
|
||||
char buf[SMALLBUFSIZE];
|
||||
snprintf(buf, sizeof(buf)-1, "%s", network_addr_prefix);
|
||||
|
||||
*p = '\0';
|
||||
prefix = atoi(++p);
|
||||
|
||||
if(a_to_hl(network_addr_prefix, &net)){
|
||||
smtp_acl->low = network(net, prefix);
|
||||
smtp_acl->high = broadcast(net, prefix);
|
||||
smtp_acl->prefix = prefix;
|
||||
|
||||
syslog(LOG_PRIORITY, "info: parsed acl *%s* to low: %u, high: %u, prefix: %d, reject: %d", buf, smtp_acl->low, smtp_acl->high, smtp_acl->prefix, smtp_acl->rejected);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void load_smtp_acl(struct smtp_acl *smtp_acl[]){
|
||||
int count=0;
|
||||
|
||||
clear_smtp_acl(smtp_acl);
|
||||
init_smtp_acl(smtp_acl);
|
||||
|
||||
FILE *f = fopen(SMTP_ACL_FILE, "r");
|
||||
if(!f){
|
||||
syslog(LOG_PRIORITY, "info: cannot open %s, piler-smtp accepts smtp connections from everywhere", SMTP_ACL_FILE);
|
||||
return;
|
||||
}
|
||||
|
||||
char line[SMALLBUFSIZE];
|
||||
struct smtp_acl acl;
|
||||
|
||||
while(fgets(line, sizeof(line)-1, f)){
|
||||
int rc = is_valid_line(line);
|
||||
if(rc < 0){
|
||||
syslog(LOG_PRIORITY, "warn: invalid network range: *%s*", line);
|
||||
}
|
||||
|
||||
if(rc == 1 && str_to_net_range(line, &acl) == 1){
|
||||
add_smtp_acl(smtp_acl, line, &acl);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
// If we have entries on the smtp acl list, then add 127.0.0.1/8
|
||||
if(count){
|
||||
snprintf(line, sizeof(line)-1, "127.0.0.1/8 permit");
|
||||
|
||||
if(str_to_net_range(line, &acl) == 1){
|
||||
add_smtp_acl(smtp_acl, line, &acl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int is_blocked_by_pilerscreen(struct smtp_acl *smtp_acl[], char *ipaddr, struct config *cfg){
|
||||
struct smtp_acl *q;
|
||||
in_addr_t addr = 0;
|
||||
|
||||
if(a_to_hl(ipaddr, &addr) == 0){
|
||||
syslog(LOG_PRIORITY, "error: invalid smtp client address: *%s*", ipaddr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
q = smtp_acl[0];
|
||||
|
||||
// Empty network ranges list
|
||||
if(!q){
|
||||
syslog(LOG_PRIORITY, "info: empty network ranges list, pass");
|
||||
return 0;
|
||||
}
|
||||
|
||||
while(q){
|
||||
if(addr >= q->low && addr <= q->high){
|
||||
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "info: smtp client %s is on %s/%d rejected: %d", ipaddr, q->network_str, q->prefix, q->rejected);
|
||||
return q->rejected;
|
||||
}
|
||||
|
||||
q = q->r;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
16
src/screen.h
Normal file
16
src/screen.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* rules.h, SJ
|
||||
*/
|
||||
|
||||
#ifndef _NETRANGE_H
|
||||
#define _NETRANGE_H
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
void init_smtp_acl(struct smtp_acl *smtp_acl[]);
|
||||
void clear_smtp_acl(struct smtp_acl *smtp_acl[]);
|
||||
int add_smtp_acl(struct smtp_acl *smtp_acl[], char *network_str, struct smtp_acl *acl);
|
||||
void load_smtp_acl(struct smtp_acl *smtp_acl[]);
|
||||
int is_blocked_by_pilerscreen(struct smtp_acl *smtp_acl[], char *ipaddr, struct config *cfg);
|
||||
|
||||
#endif /* _NETRANGE_H */
|
@ -10,26 +10,7 @@ int get_session_slot(struct smtp_session **sessions, int max_connections);
|
||||
void init_smtp_session(struct smtp_session *session, int slot, int sd, struct config *cfg);
|
||||
|
||||
|
||||
#ifdef HAVE_LIBWRAP
|
||||
int is_blocked_by_tcp_wrappers(int sd){
|
||||
struct request_info req;
|
||||
|
||||
request_init(&req, RQ_DAEMON, "piler", RQ_FILE, sd, 0);
|
||||
|
||||
fromhost(&req);
|
||||
|
||||
if(!hosts_access(&req)){
|
||||
send(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 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int start_new_session(struct smtp_session **sessions, int socket, int *num_connections, struct config *cfg){
|
||||
int start_new_session(struct smtp_session **sessions, int socket, int *num_connections, struct smtp_acl *smtp_acl[], char *client_addr, struct config *cfg){
|
||||
int slot;
|
||||
|
||||
/*
|
||||
@ -43,12 +24,13 @@ int start_new_session(struct smtp_session **sessions, int socket, int *num_conne
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBWRAP
|
||||
if(is_blocked_by_tcp_wrappers(socket) == 1){
|
||||
// Check remote client against the allowed network ranges
|
||||
if(cfg->smtp_access_list && is_blocked_by_pilerscreen(smtp_acl, client_addr, cfg)){
|
||||
send(socket, SMTP_RESP_550_ERR, strlen(SMTP_RESP_550_ERR), 0);
|
||||
close(socket);
|
||||
syslog(LOG_PRIORITY, "denied connection from %s by %s", client_addr, SMTP_ACL_FILE);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
slot = get_session_slot(sessions, cfg->max_connections);
|
||||
|
||||
|
@ -55,6 +55,7 @@
|
||||
#define SMTP_RESP_502_ERR "502 Command not implemented\r\n"
|
||||
#define SMTP_RESP_503_ERR "503 Bad command sequence\r\n"
|
||||
#define SMTP_RESP_550_ERR_YOU_ARE_BANNED_BY_LOCAL_POLICY "550 You are banned by local policy\r\n"
|
||||
#define SMTP_RESP_550_ERR "550 Service currently unavailable\r\n"
|
||||
|
||||
|
||||
// LMTP commands
|
||||
|
Loading…
Reference in New Issue
Block a user