2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 21:55:20 +00:00

snapshot-20011015

This commit is contained in:
Wietse Venema
2001-10-15 00:00:00 -05:00
committed by Viktor Dukhovni
parent 6f7a3806f8
commit d9c14900c6
29 changed files with 639 additions and 75 deletions

View File

@@ -5490,7 +5490,17 @@ Apologies for any names omitted.
20011010-14 20011010-14
Replaced the internal protocols by (name,value) attribute Replaced the internal protocols by (name,value) attribute
lists. This is more extensible. lists. This gives better error detection when we start
making changes to internal protocols.
20011015
Put base 64 encoding into place on the replced internal
protocols. Files: util/base64_code.[hc].
Feature: header/body REJECT rules can now end in text that
is sent to the originator. Files: cleanup/cleanup.c,
cleanup/cleanup_message.c, conf/sample-filter.cf.
Open problems: Open problems:

View File

@@ -13,6 +13,8 @@
# #
# REJECT the entire message is rejected. # REJECT the entire message is rejected.
# #
# REJECT text.... The text is sent to the originator.
#
# IGNORE the header line is silently discarded. # IGNORE the header line is silently discarded.
# #
# OK Nothing happens. the message will still be rejected when some # OK Nothing happens. the message will still be rejected when some

View File

@@ -142,11 +142,12 @@ static int bounce_append_proto(char *service_name, VSTREAM *client)
/* /*
* Read the and validate the client request. * Read the and validate the client request.
*/ */
if (mail_command_server(client, "%d %s %s %s", if (mail_command_server(client,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags, ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient, ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
ATTR_TYPE_STR, MAIL_ATTR_WHY, why, 0) != 4) { ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
ATTR_TYPE_END) != 4) {
msg_warn("malformed request"); msg_warn("malformed request");
return (-1); return (-1);
} }
@@ -181,11 +182,12 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client, int flush)
/* /*
* Read and validate the client request. * Read and validate the client request.
*/ */
if (mail_command_server(client, ATTR_FLAG_MISSING, if (mail_command_server(client,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags, ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name, ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, 0) != 4) { ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
ATTR_TYPE_END) != 4) {
msg_warn("malformed request"); msg_warn("malformed request");
return (-1); return (-1);
} }
@@ -230,7 +232,8 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client, int flush)
ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name, ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp_delims, 0) != 5) { ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp_delims,
ATTR_TYPE_END) != 5) {
msg_warn("malformed request"); msg_warn("malformed request");
return (-1); return (-1);
} }

View File

@@ -235,8 +235,10 @@ static void cleanup_service(VSTREAM *src, char *unused_service, char **argv)
*/ */
attr_print(src, ATTR_FLAG_NONE, attr_print(src, ATTR_FLAG_NONE,
ATTR_TYPE_NUM, MAIL_ATTR_STATUS, cleanup_close(state), ATTR_TYPE_NUM, MAIL_ATTR_STATUS, cleanup_close(state),
ATTR_TYPE_STR, MAIL_ATTR_WHY, "", ATTR_TYPE_STR, MAIL_ATTR_WHY, state->why_rejected ?
vstring_str(state->why_rejected) : "",
ATTR_TYPE_END); ATTR_TYPE_END);
cleanup_free(state);
/* /*
* Cleanup. * Cleanup.

View File

@@ -59,6 +59,7 @@ typedef struct CLEANUP_STATE {
off_t xtra_offset; /* start of extra segment */ off_t xtra_offset; /* start of extra segment */
int end_seen; /* REC_TYPE_END seen */ int end_seen; /* REC_TYPE_END seen */
int rcpt_count; /* recipient count */ int rcpt_count; /* recipient count */
VSTRING *why_rejected; /* REJECT reason */
} CLEANUP_STATE; } CLEANUP_STATE;
/* /*
@@ -104,6 +105,7 @@ extern void cleanup_state_free(CLEANUP_STATE *);
extern CLEANUP_STATE *cleanup_open(void); extern CLEANUP_STATE *cleanup_open(void);
extern void cleanup_control(CLEANUP_STATE *, int); extern void cleanup_control(CLEANUP_STATE *, int);
extern int cleanup_close(CLEANUP_STATE *); extern int cleanup_close(CLEANUP_STATE *);
extern void cleanup_free(CLEANUP_STATE *);
extern void cleanup_all(void); extern void cleanup_all(void);
extern void cleanup_pre_jail(char *, char **); extern void cleanup_pre_jail(char *, char **);
extern void cleanup_post_jail(char *, char **); extern void cleanup_post_jail(char *, char **);

View File

@@ -20,6 +20,9 @@
/* /*
/* int cleanup_close(state) /* int cleanup_close(state)
/* CLEANUP_STATE *state; /* CLEANUP_STATE *state;
/*
/* int cleanup_free(state)
/* CLEANUP_STATE *state;
/* DESCRIPTION /* DESCRIPTION
/* This module implements a callable interface to the cleanup service /* This module implements a callable interface to the cleanup service
/* for processing one message and for writing it to queue file. /* for processing one message and for writing it to queue file.
@@ -27,7 +30,8 @@
/* /*
/* cleanup_open() creates a new queue file and performs other /* cleanup_open() creates a new queue file and performs other
/* per-message initialization. The result is a handle that should be /* per-message initialization. The result is a handle that should be
/* given to the cleanup_control(), cleanup_record() and cleanup_close() /* given to the cleanup_control(), cleanup_record(), cleanup_close()
/* and cleanup_close()
/* routines. The name of the queue file is in the queue_id result /* routines. The name of the queue file is in the queue_id result
/* structure member. /* structure member.
/* /*
@@ -43,10 +47,12 @@
/* The result is false when further message processing is futile. /* The result is false when further message processing is futile.
/* In that case, it is safe to call cleanup_close() immediately. /* In that case, it is safe to call cleanup_close() immediately.
/* /*
/* cleanup_close() finishes a queue file. In case of any errors, /* cleanup_close() closes a queue file. In case of any errors,
/* the file is removed. The result value is non-zero in case of /* the file is removed. The result value is non-zero in case of
/* problems. Use cleanup_strerror() to translate the result into /* problems. Use cleanup_strerror() to translate the result into
/* human_readable text. /* human_readable text.
/*
/* cleanup_free() destroys its argument.
/* DIAGNOSTICS /* DIAGNOSTICS
/* Problems and transactions are logged to \fBsyslogd\fR(8). /* Problems and transactions are logged to \fBsyslogd\fR(8).
/* SEE ALSO /* SEE ALSO
@@ -161,6 +167,7 @@ int cleanup_close(CLEANUP_STATE *state)
{ {
char *junk; char *junk;
int status; int status;
const char *reason;
/* /*
* See if there are any errors. For example, the message is incomplete, * See if there are any errors. For example, the message is incomplete,
@@ -181,7 +188,7 @@ int cleanup_close(CLEANUP_STATE *state)
* copy of the message. * copy of the message.
*/ */
if ((state->errs & CLEANUP_STAT_LETHAL) == 0) if ((state->errs & CLEANUP_STAT_LETHAL) == 0)
state->errs |= mail_stream_finish(state->handle); state->errs |= mail_stream_finish(state->handle, (VSTRING *) 0);
else else
mail_stream_cleanup(state->handle); mail_stream_cleanup(state->handle);
state->handle = 0; state->handle = 0;
@@ -216,11 +223,14 @@ int cleanup_close(CLEANUP_STATE *state)
if (state->errs & CLEANUP_STAT_LETHAL) { if (state->errs & CLEANUP_STAT_LETHAL) {
if (CAN_BOUNCE()) { if (CAN_BOUNCE()) {
reason = cleanup_strerror(state->errs);
if (reason == cleanup_strerror(CLEANUP_STAT_CONT))
reason = vstring_str(state->why_rejected);
if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id, if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id,
state->recip ? state->recip : "unknown", state->recip ? state->recip : "unknown",
"cleanup", state->time, "cleanup", state->time,
"Message processing aborted: %s", "Message processing aborted: %s",
cleanup_strerror(state->errs)) == 0 reason) == 0
&& bounce_flush(BOUNCE_FLAG_CLEAN, MAIL_QUEUE_INCOMING, && bounce_flush(BOUNCE_FLAG_CLEAN, MAIL_QUEUE_INCOMING,
state->queue_id, state->sender) == 0) { state->queue_id, state->sender) == 0) {
state->errs = 0; state->errs = 0;
@@ -249,6 +259,12 @@ int cleanup_close(CLEANUP_STATE *state)
if (msg_verbose) if (msg_verbose)
msg_info("cleanup_close: status %d", state->errs); msg_info("cleanup_close: status %d", state->errs);
status = state->errs & CLEANUP_STAT_LETHAL; status = state->errs & CLEANUP_STAT_LETHAL;
cleanup_state_free(state);
return (status); return (status);
} }
/* cleanup_close - pay the last respects */
void cleanup_free(CLEANUP_STATE *state)
{
cleanup_state_free(state);
}

View File

@@ -254,6 +254,38 @@ static void cleanup_rewrite_recip(CLEANUP_STATE *state, HEADER_OPTS *hdr_opts)
cleanup_fold_header(state); cleanup_fold_header(state);
} }
/* cleanup_parse_reject - parse REJECT liune and pick up the reason */
static const char *cleanup_parse_reject(CLEANUP_STATE *state, const char *value)
{
const char *reason;
/*
* See if they spelled REJECT right.
*/
if (strcasecmp(value, "REJECT") == 0) {
reason = "Content rejected";
} else if (strncasecmp(value, "REJECT ", 7) == 0
|| strncasecmp(value, "REJECT\t", 7) == 0) {
reason = value + 7;
while (*reason && ISSPACE(*reason))
reason++;
if (*reason == 0)
reason = "Content rejected";
} else {
return (0);
}
/*
* Update the remembered reason if none was stored.
*/
if (state->why_rejected == 0) {
state->why_rejected = vstring_alloc(10);
vstring_strcpy(state->why_rejected, reason);
}
return (reason);
}
/* cleanup_header - process one complete header line */ /* cleanup_header - process one complete header line */
static void cleanup_header(CLEANUP_STATE *state) static void cleanup_header(CLEANUP_STATE *state)
@@ -267,12 +299,13 @@ static void cleanup_header(CLEANUP_STATE *state)
if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_header_checks) { if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_header_checks) {
char *header = vstring_str(state->header_buf); char *header = vstring_str(state->header_buf);
const char *value; const char *value;
const char *reason;
if ((value = maps_find(cleanup_header_checks, header, 0)) != 0) { if ((value = maps_find(cleanup_header_checks, header, 0)) != 0) {
if (strcasecmp(value, "REJECT") == 0) { if ((reason = cleanup_parse_reject(state, value)) != 0) {
msg_info("%s: reject: header %.200s; from=<%s> to=<%s>", msg_info("%s: reject: header %.200s; from=<%s> to=<%s>: %s",
state->queue_id, header, state->sender, state->queue_id, header, state->sender,
state->recip ? state->recip : "unknown"); state->recip ? state->recip : "unknown", reason);
state->errs |= CLEANUP_STAT_CONT; state->errs |= CLEANUP_STAT_CONT;
} else if (strcasecmp(value, "IGNORE") == 0) { } else if (strcasecmp(value, "IGNORE") == 0) {
return; return;
@@ -547,12 +580,13 @@ static void cleanup_message_body(CLEANUP_STATE *state, int type, char *buf, int
*/ */
if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_body_checks) { if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_body_checks) {
const char *value; const char *value;
const char *reason;
if ((value = maps_find(cleanup_body_checks, buf, 0)) != 0) { if ((value = maps_find(cleanup_body_checks, buf, 0)) != 0) {
if (strcasecmp(value, "REJECT") == 0) { if ((reason = cleanup_parse_reject(state, value)) != 0) {
msg_info("%s: reject: body %.200s; from=<%s> to=<%s>", msg_info("%s: reject: body %.200s; from=<%s> to=<%s>: %s",
state->queue_id, buf, state->sender, state->queue_id, buf, state->sender,
state->recip ? state->recip : "unknown"); state->recip ? state->recip : "unknown", reason);
state->errs |= CLEANUP_STAT_CONT; state->errs |= CLEANUP_STAT_CONT;
} else if (strcasecmp(value, "IGNORE") == 0) { } else if (strcasecmp(value, "IGNORE") == 0) {
return; return;

View File

@@ -84,6 +84,7 @@ CLEANUP_STATE *cleanup_state_alloc(void)
state->xtra_offset = -1; state->xtra_offset = -1;
state->end_seen = 0; state->end_seen = 0;
state->rcpt_count = 0; state->rcpt_count = 0;
state->why_rejected = 0;
return (state); return (state);
} }
@@ -113,5 +114,7 @@ void cleanup_state_free(CLEANUP_STATE *state)
if (state->queue_id) if (state->queue_id)
myfree(state->queue_id); myfree(state->queue_id);
been_here_free(state->dups); been_here_free(state->dups);
if (state->why_rejected)
vstring_free(state->why_rejected);
myfree((char *) state); myfree((char *) state);
} }

View File

@@ -547,8 +547,8 @@ static void flush_service(VSTREAM *client_stream, char *unused_service,
site = vstring_alloc(10); site = vstring_alloc(10);
queue_id = vstring_alloc(10); queue_id = vstring_alloc(10);
if (attr_scan(client_stream, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA, if (attr_scan(client_stream, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
ATTR_TYPE_STR, MAIL_ATTR_SITE, site, ATTR_FLAG_MISSING, ATTR_TYPE_STR, MAIL_ATTR_SITE, site,
ATTR_TYPE_STR, MAIL_ATTR_SITE, queue_id, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
ATTR_TYPE_END) == 2 ATTR_TYPE_END) == 2
&& mail_queue_id_ok(STR(queue_id))) && mail_queue_id_ok(STR(queue_id)))
status = flush_add_service(lowercase(STR(site)), STR(queue_id)); status = flush_add_service(lowercase(STR(site)), STR(queue_id));

View File

@@ -144,7 +144,7 @@ int vbounce_append(int flags, const char *id, const char *recipient,
vstring_vsprintf(why, fmt, ap); vstring_vsprintf(why, fmt, ap);
if (mail_command_client(MAIL_CLASS_PRIVATE, var_soft_bounce ? if (mail_command_client(MAIL_CLASS_PRIVATE, var_soft_bounce ?
MAIL_SERVICE_DEFER : MAIL_SERVICE_BOUNCE, MAIL_SERVICE_DEFER : MAIL_SERVICE_BOUNCE,
ATTR_TYPE_NUM, MAIL_ATTR_REQ, BOUNCE_CMD_APPEND, ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags, ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient, ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
@@ -177,7 +177,7 @@ int bounce_flush(int flags, const char *queue, const char *id,
if (var_soft_bounce) if (var_soft_bounce)
return (-1); return (-1);
if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_BOUNCE, if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_BOUNCE,
ATTR_TYPE_NUM, MAIL_ATTR_REQ, BOUNCE_CMD_FLUSH, ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_FLUSH,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags, ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue, ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,

View File

@@ -146,7 +146,7 @@ int vdefer_append(int flags, const char *id, const char *recipient,
vstring_vsprintf(why, fmt, ap); vstring_vsprintf(why, fmt, ap);
if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER, if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
ATTR_TYPE_NUM, MAIL_ATTR_REQ, BOUNCE_CMD_APPEND, ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags, ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient, ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
@@ -180,7 +180,7 @@ int defer_flush(int flags, const char *queue, const char *id,
const char *sender) const char *sender)
{ {
if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER, if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
ATTR_TYPE_NUM, MAIL_ATTR_REQ, BOUNCE_CMD_FLUSH, ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_FLUSH,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags, ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue, ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
@@ -199,7 +199,7 @@ int defer_warn(int flags, const char *queue, const char *id,
const char *sender) const char *sender)
{ {
if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER, if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
ATTR_TYPE_NUM, MAIL_ATTR_REQ, BOUNCE_CMD_WARN, ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_WARN,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags, ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue, ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,

View File

@@ -0,0 +1,39 @@
#ifndef _MAIL_ATTR_H_INCLUDED_
#define _MAIL_ATTR_H_INCLUDED_
/*++
/* NAME
/* mail_attr 3h
/* SUMMARY
/* mail internal IPC support
/* SYNOPSIS
/* #include <mail_attr.h>
/* DESCRIPTION
/* .nf
/*
* Request attribute. Values are defined by individual applications.
*/
#define MAIL_REQUEST "request"
/*
* Request completion status.
*/
#define MAIL_STATUS "status"
#define MAIL_STAT_OK "success"
#define MAIL_STAT_FAIL "failed"
#define MAIL_STAT_RETRY "retry"
#define MAIL_STAT_REJECT "reject"
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
#endif

View File

@@ -0,0 +1,69 @@
/*++
/* NAME
/* mail_command_read 3
/* SUMMARY
/* single-command server
/* SYNOPSIS
/* #include <mail_proto.h>
/*
/* int mail_command_read(stream, format, ...)
/* VSTREAM *stream;
/* char *format;
/* DESCRIPTION
/* This module implements the server interface for single-command
/* requests: a clients sends a single command and expects a single
/* completion status code.
/*
/* Arguments:
/* .IP stream
/* Server endpoint.
/* .IP format
/* Format string understood by mail_print(3) and mail_scan(3).
/* DIAGNOSTICS
/* Fatal: out of memory.
/* SEE ALSO
/* mail_scan(3)
/* mail_command_write(3) client interface
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
#include <stdarg.h>
#include <string.h>
/* Utility library. */
#include <vstring.h>
#include <vstream.h>
/* Global library. */
#include "mail_proto.h"
/* mail_command_read - read single-command request */
int mail_command_read(VSTREAM *stream, char *fmt,...)
{
VSTRING *eof = vstring_alloc(10);
va_list ap;
int count;
va_start(ap, fmt);
count = mail_vscan(stream, fmt, ap);
va_end(ap);
if (mail_scan(stream, "%s", eof) != 1 || strcmp(vstring_str(eof), MAIL_EOF))
count = -1;
vstring_free(eof);
return (count);
}

View File

@@ -0,0 +1,82 @@
/*++
/* NAME
/* mail_command_write 3
/* SUMMARY
/* single-command client
/* SYNOPSIS
/* #include <mail_proto.h>
/*
/* int mail_command_write(class, name, format, ...)
/* const char *class;
/* const char *name;
/* const char *format;
/* DESCRIPTION
/* This module implements a client interface for single-command
/* clients: a client that sends a single command and expects
/* a single completion status code.
/*
/* Arguments:
/* .IP class
/* Service type: MAIL_CLASS_PUBLIC or MAIL_CLASS_PRIVATE
/* .IP name
/* Service name (master.cf).
/* .IP format
/* Format string understood by mail_print(3).
/* DIAGNOSTICS
/* The result is -1 if the request could not be sent, otherwise
/* the result is the status reported by the server.
/* Warnings: problems connecting to the requested service.
/* Fatal: out of memory.
/* SEE ALSO
/* mail_command_read(3), server interface
/* mail_proto(5h), client-server protocol
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
#include <stdarg.h>
/* Utility library. */
#include <vstream.h>
/* Global library. */
#include "mail_proto.h"
/* mail_command_write - single-command transaction with completion status */
int mail_command_write(const char *class, const char *name,
const char *fmt,...)
{
va_list ap;
VSTREAM *stream;
int status;
/*
* Talk a little protocol with the specified service.
*/
if ((stream = mail_connect(class, name, BLOCKING)) == 0)
return (-1);
va_start(ap, fmt);
status = mail_vprint(stream, fmt, ap);
va_end(ap);
if (status != 0
|| mail_print(stream, "%s", MAIL_EOF) != 0
|| vstream_fflush(stream) != 0
|| mail_scan(stream, "%d", &status) != 1)
status = -1;
(void) vstream_fclose(stream);
return (status);
}

View File

@@ -66,6 +66,7 @@
#include <connect.h> #include <connect.h>
#include <mymalloc.h> #include <mymalloc.h>
#include <iostuff.h> #include <iostuff.h>
#include <stringops.h>
/* Global library. */ /* Global library. */
@@ -79,6 +80,7 @@ VSTREAM *mail_connect(const char *class, const char *name, int block_mode)
char *path; char *path;
VSTREAM *stream; VSTREAM *stream;
int fd; int fd;
char *sock_name;
path = mail_pathname(class, name); path = mail_pathname(class, name);
if ((fd = LOCAL_CONNECT(path, block_mode, 0)) < 0) { if ((fd = LOCAL_CONNECT(path, block_mode, 0)) < 0) {
@@ -90,9 +92,11 @@ VSTREAM *mail_connect(const char *class, const char *name, int block_mode)
msg_info("connect to subsystem %s", path); msg_info("connect to subsystem %s", path);
stream = vstream_fdopen(fd, O_RDWR); stream = vstream_fdopen(fd, O_RDWR);
timed_ipc_setup(stream); timed_ipc_setup(stream);
sock_name = concatenate("socket ", path, (char *) 0);
vstream_control(stream, vstream_control(stream,
VSTREAM_CTL_PATH, path, VSTREAM_CTL_PATH, sock_name,
VSTREAM_CTL_END); VSTREAM_CTL_END);
myfree(sock_name);
} }
myfree(path); myfree(path);
return (stream); return (stream);

View File

@@ -116,7 +116,7 @@ void mail_stream_cleanup(MAIL_STREAM * info)
/* mail_stream_finish_file - finish file mail stream */ /* mail_stream_finish_file - finish file mail stream */
static int mail_stream_finish_file(MAIL_STREAM * info) static int mail_stream_finish_file(MAIL_STREAM * info, VSTRING *unused_why)
{ {
int status = 0; int status = 0;
static char wakeup[] = {TRIGGER_REQ_WAKEUP}; static char wakeup[] = {TRIGGER_REQ_WAKEUP};
@@ -161,7 +161,7 @@ static int mail_stream_finish_file(MAIL_STREAM * info)
/* mail_stream_finish_ipc - finish IPC mail stream */ /* mail_stream_finish_ipc - finish IPC mail stream */
static int mail_stream_finish_ipc(MAIL_STREAM * info) static int mail_stream_finish_ipc(MAIL_STREAM * info, VSTRING *why)
{ {
int status = CLEANUP_STAT_WRITE; int status = CLEANUP_STAT_WRITE;
@@ -169,7 +169,9 @@ static int mail_stream_finish_ipc(MAIL_STREAM * info)
* Receive the peer's completion status. * Receive the peer's completion status.
*/ */
if (attr_scan(info->stream, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA, if (attr_scan(info->stream, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status, 0) != 1) ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
ATTR_TYPE_END) != 2)
status = CLEANUP_STAT_WRITE; status = CLEANUP_STAT_WRITE;
/* /*
@@ -181,9 +183,9 @@ static int mail_stream_finish_ipc(MAIL_STREAM * info)
/* mail_stream_finish - finish action */ /* mail_stream_finish - finish action */
int mail_stream_finish(MAIL_STREAM * info) int mail_stream_finish(MAIL_STREAM * info, VSTRING *why)
{ {
return (info->finish(info)); return (info->finish(info, why));
} }
/* mail_stream_file - destination is file */ /* mail_stream_file - destination is file */

View File

@@ -22,7 +22,7 @@
*/ */
typedef struct MAIL_STREAM MAIL_STREAM; typedef struct MAIL_STREAM MAIL_STREAM;
typedef int (*MAIL_STREAM_FINISH_FN) (MAIL_STREAM *); typedef int (*MAIL_STREAM_FINISH_FN) (MAIL_STREAM *, VSTRING *);
typedef int (*MAIL_STREAM_CLOSE_FN) (VSTREAM *); typedef int (*MAIL_STREAM_CLOSE_FN) (VSTREAM *);
struct MAIL_STREAM { struct MAIL_STREAM {
@@ -38,7 +38,7 @@ extern MAIL_STREAM *mail_stream_file(const char *, const char *, const char *);
extern MAIL_STREAM *mail_stream_service(const char *, const char *); extern MAIL_STREAM *mail_stream_service(const char *, const char *);
extern MAIL_STREAM *mail_stream_command(const char *); extern MAIL_STREAM *mail_stream_command(const char *);
extern void mail_stream_cleanup(MAIL_STREAM *); extern void mail_stream_cleanup(MAIL_STREAM *);
extern int mail_stream_finish(MAIL_STREAM *); extern int mail_stream_finish(MAIL_STREAM *, VSTRING *);
/* LICENSE /* LICENSE

View File

@@ -15,7 +15,7 @@
* Version of this program. * Version of this program.
*/ */
#define VAR_MAIL_VERSION "mail_version" #define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "Snapshot-20011014" #define DEF_MAIL_VERSION "Snapshot-20011015"
extern char *var_mail_version; extern char *var_mail_version;
/* LICENSE /* LICENSE

View File

@@ -289,7 +289,7 @@ int main(int argc, char **argv)
/* /*
* Finish the file. * Finish the file.
*/ */
if ((status = mail_stream_finish(dst)) != 0) if ((status = mail_stream_finish(dst, (VSTRING *) 0)) != 0)
msg_fatal("uid=%ld: %s", (long) uid, cleanup_strerror(status)); msg_fatal("uid=%ld: %s", (long) uid, cleanup_strerror(status));
/* /*

View File

@@ -394,7 +394,7 @@ static void qmqpd_close_file(QMQPD_STATE *state)
* Finish the queue file or finish the cleanup conversation. * Finish the queue file or finish the cleanup conversation.
*/ */
if (state->err == 0) if (state->err == 0)
state->err = mail_stream_finish(state->dest); state->err = mail_stream_finish(state->dest, state->why_rejected);
else else
mail_stream_cleanup(state->dest); mail_stream_cleanup(state->dest);
state->dest = 0; state->dest = 0;
@@ -453,7 +453,7 @@ static int qmqpd_send_status(QMQPD_STATE *state)
"Error: too many hops"); "Error: too many hops");
} else if ((state->err & CLEANUP_STAT_CONT) != 0) { } else if ((state->err & CLEANUP_STAT_CONT) != 0) {
qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD, qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
"Error: content rejected"); "Error: %s", STR(state->why_rejected));
} else if ((state->err & CLEANUP_STAT_WRITE) != 0) { } else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY, qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY,
"Error: queue file write error"); "Error: queue file write error");

View File

@@ -45,6 +45,7 @@ typedef struct {
char *recipient; /* recipient address */ char *recipient; /* recipient address */
char *protocol; /* protocol name */ char *protocol; /* protocol name */
char *where; /* protocol state */ char *where; /* protocol state */
VSTRING *why_rejected; /* REJECT reason */
} QMQPD_STATE; } QMQPD_STATE;
/* /*

View File

@@ -74,6 +74,7 @@ QMQPD_STATE *qmqpd_state_alloc(VSTREAM *stream)
state->recipient = 0; state->recipient = 0;
state->protocol = "QMQP"; state->protocol = "QMQP";
state->where = "initializing client connection"; state->where = "initializing client connection";
state->why_rejected = vstring_alloc(10);
return (state); return (state);
} }
@@ -92,5 +93,6 @@ void qmqpd_state_free(QMQPD_STATE *state)
myfree(state->sender); myfree(state->sender);
if (state->recipient) if (state->recipient)
myfree(state->recipient); myfree(state->recipient);
vstring_free(state->why_rejected);
myfree((char *) state); myfree((char *) state);
} }

View File

@@ -516,7 +516,7 @@ static void enqueue(const int flags, const char *sender, const char *full_name,
if (vstream_ferror(VSTREAM_IN)) if (vstream_ferror(VSTREAM_IN))
msg_fatal("%s(%ld): error reading input: %m", msg_fatal("%s(%ld): error reading input: %m",
saved_sender, (long) uid); saved_sender, (long) uid);
if ((status = mail_stream_finish(handle)) != 0) if ((status = mail_stream_finish(handle, buf)) != 0)
msg_fatal("%s(%ld): %s", saved_sender, msg_fatal("%s(%ld): %s", saved_sender,
(long) uid, cleanup_strerror(status)); (long) uid, cleanup_strerror(status));
if (sendmail_path) { if (sendmail_path) {

View File

@@ -891,6 +891,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
int curr_rec_type; int curr_rec_type;
int prev_rec_type; int prev_rec_type;
int first = 1; int first = 1;
VSTRING *why = 0;
/* /*
* Sanity checks. With ESMTP command pipelining the client can send DATA * Sanity checks. With ESMTP command pipelining the client can send DATA
@@ -999,7 +1000,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
* Finish the queue file or finish the cleanup conversation. * Finish the queue file or finish the cleanup conversation.
*/ */
if (state->err == 0) if (state->err == 0)
state->err |= mail_stream_finish(state->dest); state->err |= mail_stream_finish(state->dest, why = vstring_alloc(10));
else else
mail_stream_cleanup(state->dest); mail_stream_cleanup(state->dest);
state->dest = 0; state->dest = 0;
@@ -1040,7 +1041,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
smtpd_chat_reply(state, "554 Error: too many hops"); smtpd_chat_reply(state, "554 Error: too many hops");
} else if ((state->err & CLEANUP_STAT_CONT) != 0) { } else if ((state->err & CLEANUP_STAT_CONT) != 0) {
state->error_mask |= MAIL_ERROR_POLICY; state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "552 Error: content rejected"); smtpd_chat_reply(state, "552 Error: %s", STR(why));
} else if ((state->err & CLEANUP_STAT_WRITE) != 0) { } else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
state->error_mask |= MAIL_ERROR_RESOURCE; state->error_mask |= MAIL_ERROR_RESOURCE;
smtpd_chat_reply(state, "451 Error: queue file write error"); smtpd_chat_reply(state, "451 Error: queue file write error");
@@ -1062,6 +1063,8 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
*/ */
mail_reset(state); mail_reset(state);
rcpt_reset(state); rcpt_reset(state);
if (why)
vstring_free(why);
return (state->err); return (state->err);
} }

View File

@@ -24,7 +24,7 @@ SRCS = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \
sane_link.c unescape.c timed_read.c timed_write.c dict_tcp.c \ sane_link.c unescape.c timed_read.c timed_write.c dict_tcp.c \
hex_quote.c dict_alloc.c rand_sleep.c sane_time.c dict_debug.c \ hex_quote.c dict_alloc.c rand_sleep.c sane_time.c dict_debug.c \
sane_socketpair.c myrand.c netstring.c ctable.c attr_print.c intv.c \ sane_socketpair.c myrand.c netstring.c ctable.c attr_print.c intv.c \
attr_scan.c attr_table.c attr_scan.c attr_table.c base64_code.c
OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \ OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \ close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \
dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \ dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \
@@ -50,7 +50,7 @@ OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
sane_link.o unescape.o timed_read.o timed_write.o dict_tcp.o \ sane_link.o unescape.o timed_read.o timed_write.o dict_tcp.o \
hex_quote.o dict_alloc.o rand_sleep.o sane_time.o dict_debug.o \ hex_quote.o dict_alloc.o rand_sleep.o sane_time.o dict_debug.o \
sane_socketpair.o myrand.o netstring.o ctable.o attr_print.o intv.o \ sane_socketpair.o myrand.o netstring.o ctable.o attr_print.o intv.o \
attr_scan.o attr_table.o attr_scan.o attr_table.o base64_code.o
HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \ HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \ dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \
dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \ dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \
@@ -67,7 +67,7 @@ HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
dict_unix.h dict_pcre.h dict_regexp.h mac_expand.h clean_env.h \ dict_unix.h dict_pcre.h dict_regexp.h mac_expand.h clean_env.h \
watchdog.h spawn_command.h sane_fsops.h dict_tcp.h hex_quote.h \ watchdog.h spawn_command.h sane_fsops.h dict_tcp.h hex_quote.h \
sane_time.h sane_socketpair.h myrand.h netstring.h ctable.h \ sane_time.h sane_socketpair.h myrand.h netstring.h ctable.h \
intv.h intv.h base64_code.h
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \ TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
stream_test.c dup2_pass_on_exec.c stream_test.c dup2_pass_on_exec.c
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
@@ -84,7 +84,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
mystrtok sigdelay translit valid_hostname vstream_popen \ mystrtok sigdelay translit valid_hostname vstream_popen \
vstring vstring_vstream doze select_bug stream_test mac_expand \ vstring vstring_vstream doze select_bug stream_test mac_expand \
watchdog unescape hex_quote name_mask rand_sleep sane_time ctable \ watchdog unescape hex_quote name_mask rand_sleep sane_time ctable \
inet_addr_list attr_print attr_scan attr_table inet_addr_list attr_print attr_scan attr_table base64_code
LIB_DIR = ../../lib LIB_DIR = ../../lib
INC_DIR = ../../include INC_DIR = ../../include
@@ -303,6 +303,11 @@ attr_table: $(LIB) $@.o
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o mv junk $@.o
base64_code: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
depend: $(MAKES) depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \ (sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \ set -e; for i in [a-z][a-z0-9]*.c; do \
@@ -315,7 +320,7 @@ stream_test: stream_test.c $(LIB)
$(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS) $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS)
tests: valid_hostname_test mac_expand_test dict_test unescape_test \ tests: valid_hostname_test mac_expand_test dict_test unescape_test \
hex_quote_test ctable_test inet_addr_list_test hex_quote_test ctable_test inet_addr_list_test base64_code_test
valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref
./valid_hostname <valid_hostname.in 2>valid_hostname.tmp ./valid_hostname <valid_hostname.in 2>valid_hostname.tmp
@@ -348,6 +353,9 @@ inet_addr_list_test: inet_addr_list
diff inet_addr_list.ref inet_addr_list.tmp diff inet_addr_list.ref inet_addr_list.tmp
rm -f inet_addr_list.tmp rm -f inet_addr_list.tmp
base64_code_test: base64_code
./base64_code
DB_TYPE = `../postconf/postconf -h default_database_type` DB_TYPE = `../postconf/postconf -h default_database_type`
dict_test: dict_open testdb dict_test.in dict_test.ref dict_test: dict_open testdb dict_test.in dict_test.ref
@@ -387,6 +395,8 @@ attr_print.o: mymalloc.h
attr_print.o: vstream.h attr_print.o: vstream.h
attr_print.o: vbuf.h attr_print.o: vbuf.h
attr_print.o: htable.h attr_print.o: htable.h
attr_print.o: base64_code.h
attr_print.o: vstring.h
attr_print.o: attr.h attr_print.o: attr.h
attr_scan.o: attr_scan.c attr_scan.o: attr_scan.c
attr_scan.o: sys_defs.h attr_scan.o: sys_defs.h
@@ -397,6 +407,7 @@ attr_scan.o: vbuf.h
attr_scan.o: vstring.h attr_scan.o: vstring.h
attr_scan.o: argv.h attr_scan.o: argv.h
attr_scan.o: intv.h attr_scan.o: intv.h
attr_scan.o: base64_code.h
attr_scan.o: attr.h attr_scan.o: attr.h
attr_scan.o: htable.h attr_scan.o: htable.h
attr_table.o: attr_table.c attr_table.o: attr_table.c
@@ -411,6 +422,13 @@ attr_table.o: vstring_vstream.h
attr_table.o: argv.h attr_table.o: argv.h
attr_table.o: intv.h attr_table.o: intv.h
attr_table.o: attr.h attr_table.o: attr.h
base64_code.o: base64_code.c
base64_code.o: sys_defs.h
base64_code.o: msg.h
base64_code.o: mymalloc.h
base64_code.o: vstring.h
base64_code.o: vbuf.h
base64_code.o: base64_code.h
basename.o: basename.c basename.o: basename.c
basename.o: sys_defs.h basename.o: sys_defs.h
basename.o: stringops.h basename.o: stringops.h

View File

@@ -92,17 +92,34 @@
#include <mymalloc.h> #include <mymalloc.h>
#include <vstream.h> #include <vstream.h>
#include <htable.h> #include <htable.h>
#include <base64_code.h>
#include <attr.h> #include <attr.h>
/* attr_fprintf - encode attribute information on the fly */ #define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
static void PRINTFLIKE(2, 3) attr_fprintf(VSTREAM *fp, const char *format,...) /* attr_print_str - encode and send attribute information */
static void attr_print_str(VSTREAM *fp, const char *str, int len)
{ {
va_list ap; static VSTRING *base64_buf;
va_start(ap, format); if (base64_buf == 0)
vstream_vfprintf(fp, format, ap); base64_buf = vstring_alloc(10);
va_end(ap);
base64_encode(base64_buf, str, len);
vstream_fputs(STR(base64_buf), fp);
}
static void attr_print_num(VSTREAM *fp, unsigned num)
{
static VSTRING *plain;
if (plain == 0)
plain = vstring_alloc(10);
vstring_sprintf(plain, "%u", num);
attr_print_str(fp, STR(plain), LEN(plain));
} }
/* attr_vprint - send attribute list to stream */ /* attr_vprint - send attribute list to stream */
@@ -136,46 +153,52 @@ int attr_vprint(VSTREAM *fp, int flags, va_list ap)
switch (attr_type) { switch (attr_type) {
case ATTR_TYPE_NUM: case ATTR_TYPE_NUM:
attr_name = va_arg(ap, char *); attr_name = va_arg(ap, char *);
attr_fprintf(fp, "%s", attr_name); attr_print_str(fp, attr_name, strlen(attr_name));
int_val = va_arg(ap, int); int_val = va_arg(ap, int);
attr_fprintf(fp, ":%u", (unsigned) int_val); VSTREAM_PUTC(':', fp);
attr_print_num(fp, (unsigned) int_val);
if (msg_verbose) if (msg_verbose)
msg_info("send attr name %s value %u", attr_name, int_val); msg_info("send attr %s = %u", attr_name, int_val);
break; break;
case ATTR_TYPE_STR: case ATTR_TYPE_STR:
attr_name = va_arg(ap, char *); attr_name = va_arg(ap, char *);
attr_fprintf(fp, "%s", attr_name); attr_print_str(fp, attr_name, strlen(attr_name));
str_val = va_arg(ap, char *); str_val = va_arg(ap, char *);
attr_fprintf(fp, ":%s", str_val); VSTREAM_PUTC(':', fp);
attr_print_str(fp, str_val, strlen(str_val));
if (msg_verbose) if (msg_verbose)
msg_info("send attr name %s value %s", attr_name, str_val); msg_info("send attr %s = %s", attr_name, str_val);
break; break;
case ATTR_TYPE_NUM_ARRAY: case ATTR_TYPE_NUM_ARRAY:
attr_name = va_arg(ap, char *); attr_name = va_arg(ap, char *);
attr_fprintf(fp, "%s", attr_name); attr_print_str(fp, attr_name, strlen(attr_name));
ip_val = va_arg(ap, int *); ip_val = va_arg(ap, int *);
count_val = va_arg(ap, int); count_val = va_arg(ap, int);
for (i = 0; i < count_val; i++) for (i = 0; i < count_val; i++) {
attr_fprintf(fp, ":%u", (unsigned) *ip_val++); VSTREAM_PUTC(':', fp);
attr_print_num(fp, (unsigned) *ip_val++);}
if (msg_verbose) if (msg_verbose)
msg_info("send attr name %s values %d", attr_name, count_val); msg_info("send attr %s values %d", attr_name, count_val);
break; break;
case ATTR_TYPE_STR_ARRAY: case ATTR_TYPE_STR_ARRAY:
attr_name = va_arg(ap, char *); attr_name = va_arg(ap, char *);
attr_fprintf(fp, "%s", attr_name); attr_print_str(fp, attr_name, strlen(attr_name));
cpp_val = va_arg(ap, char **); cpp_val = va_arg(ap, char **);
count_val = va_arg(ap, int); count_val = va_arg(ap, int);
for (i = 0; i < count_val; i++) { for (i = 0; i < count_val; i++) {
str_val = *cpp_val++; str_val = *cpp_val++;
attr_fprintf(fp, ":%s", str_val); VSTREAM_PUTC(':', fp);
attr_print_str(fp, str_val, strlen(str_val));
} }
if (msg_verbose) if (msg_verbose)
msg_info("send attr name %s values %d", attr_name, count_val); msg_info("send attr %s values %d", attr_name, count_val);
break; break;
case ATTR_TYPE_HASH: case ATTR_TYPE_HASH:
ht_info_list = htable_list(va_arg(ap, HTABLE *)); ht_info_list = htable_list(va_arg(ap, HTABLE *));
for (ht = ht_info_list; *ht; ht++) { for (ht = ht_info_list; *ht; ht++) {
attr_fprintf(fp, "%s:%s", ht[0]->key, ht[0]->value); attr_print_str(fp, ht[0]->key, strlen(ht[0]->key));
VSTREAM_PUTC(':', fp);
attr_print_str(fp, ht[0]->value, strlen(ht[0]->value));
if (msg_verbose) if (msg_verbose)
msg_info("send attr name %s value %s", msg_info("send attr name %s value %s",
ht[0]->key, ht[0]->value); ht[0]->key, ht[0]->value);

View File

@@ -83,7 +83,7 @@
/* For convenience, this value requests none of the above. /* For convenience, this value requests none of the above.
/* .RE /* .RE
/* .IP type /* .IP type
/* The type determines the arguments that follow. /* The type argument determines the arguments that follow.
/* .RS /* .RS
/* .IP "ATTR_TYPE_NUM (char *, int *)" /* .IP "ATTR_TYPE_NUM (char *, int *)"
/* This argument is followed by an attribute name and an integer pointer. /* This argument is followed by an attribute name and an integer pointer.
@@ -100,15 +100,21 @@
/* This is used for recovering a string array attribute value. /* This is used for recovering a string array attribute value.
/* Values from the input stream are appended to the array. /* Values from the input stream are appended to the array.
/* .IP "ATTR_TYPE_HASH (HTABLE *)" /* .IP "ATTR_TYPE_HASH (HTABLE *)"
/* All further attributes are stored into the given hash table as simple /* All further input attributes are required to be simple string or
/* string-valued attributes, under keys equal to the attribute name. /* integer attributes.
/* Their string values are stored in the specified hash table under
/* keys equal to the attribute name (obtained from the input stream).
/* Values from the input stream are added to the hash table, but existing /* Values from the input stream are added to the hash table, but existing
/* hash table entries are not replaced. /* hash table entries are not replaced.
/* .sp /* .sp
/* N.B. This must be followed by an ATTR_TYPE_END argument. /* N.B. This construct must be followed by an ATTR_TYPE_END argument.
/* .IP ATTR_TYPE_END /* .IP ATTR_TYPE_END
/* This terminates the requested attribute list. /* This argument terminates the requested attribute list.
/* .RE /* .RE
/* BUGS
/* ATTR_TYPE_HASH accepts attributes with arbitrary names from an
/* untrusted source. This is safe only if the resulting table is
/* queried for specific names.
/* DIAGNOSTICS /* DIAGNOSTICS
/* The result value is the number of attributes that were successfully /* The result value is the number of attributes that were successfully
/* recovered from the input stream (an array-valued attribute counts /* recovered from the input stream (an array-valued attribute counts
@@ -143,6 +149,7 @@
#include <vstring.h> #include <vstring.h>
#include <argv.h> #include <argv.h>
#include <intv.h> #include <intv.h>
#include <base64_code.h>
#include <attr.h> #include <attr.h>
/* Application specific. */ /* Application specific. */
@@ -155,8 +162,12 @@
static int attr_scan_string(VSTREAM *fp, VSTRING *plain_buf, const char *context) static int attr_scan_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
{ {
static VSTRING *base64_buf = 0; static VSTRING *base64_buf = 0;
#if 0
extern int var_line_limit; /* XXX */ extern int var_line_limit; /* XXX */
int limit = var_line_limit * 5 / 4; int limit = var_line_limit * 5 / 4;
#endif
int ch; int ch;
if (base64_buf == 0) if (base64_buf == 0)
@@ -170,14 +181,16 @@ static int attr_scan_string(VSTREAM *fp, VSTRING *plain_buf, const char *context
return (-1); return (-1);
} }
VSTRING_ADDCH(base64_buf, ch); VSTRING_ADDCH(base64_buf, ch);
#if 0
if (LEN(base64_buf) > limit) { if (LEN(base64_buf) > limit) {
msg_warn("string length > %d characters from %s while reading %s", msg_warn("string length > %d characters from %s while reading %s",
limit, VSTREAM_PATH(fp), context); limit, VSTREAM_PATH(fp), context);
return (-1); return (-1);
} }
#endif
} }
VSTRING_TERMINATE(base64_buf); VSTRING_TERMINATE(base64_buf);
if (BASE64_DECODE(plain_buf, STR(base64_buf), LEN(base64_buf)) == 0) { if (base64_decode(plain_buf, STR(base64_buf), LEN(base64_buf)) == 0) {
msg_warn("malformed base64 data from %s: %.100s", msg_warn("malformed base64 data from %s: %.100s",
VSTREAM_PATH(fp), STR(base64_buf)); VSTREAM_PATH(fp), STR(base64_buf));
return (-1); return (-1);
@@ -335,7 +348,7 @@ int attr_vscan(VSTREAM *fp, int flags, va_list ap)
"attribute value")) < 0) "attribute value")) < 0)
return (conversions); return (conversions);
if (ch != '\n') { if (ch != '\n') {
msg_warn("too many values for number attribute %s from %s", msg_warn("multiple values for attribute %s from %s",
STR(name_buf), VSTREAM_PATH(fp)); STR(name_buf), VSTREAM_PATH(fp));
return (conversions); return (conversions);
} }
@@ -350,7 +363,7 @@ int attr_vscan(VSTREAM *fp, int flags, va_list ap)
if ((ch = attr_scan_string(fp, string, "attribute value")) < 0) if ((ch = attr_scan_string(fp, string, "attribute value")) < 0)
return (conversions); return (conversions);
if (ch != '\n') { if (ch != '\n') {
msg_warn("too many values for string attribute %s from %s", msg_warn("multiple values for attribute %s from %s",
STR(name_buf), VSTREAM_PATH(fp)); STR(name_buf), VSTREAM_PATH(fp));
return (conversions); return (conversions);
} }
@@ -364,7 +377,7 @@ int attr_vscan(VSTREAM *fp, int flags, va_list ap)
if ((ch = attr_scan_string(fp, str_buf, "attribute value")) < 0) if ((ch = attr_scan_string(fp, str_buf, "attribute value")) < 0)
return (conversions); return (conversions);
if (ch != '\n') { if (ch != '\n') {
msg_warn("too many values for string attribute %s from %s", msg_warn("multiple values for attribute %s from %s",
STR(name_buf), VSTREAM_PATH(fp)); STR(name_buf), VSTREAM_PATH(fp));
return (conversions); return (conversions);
} }

View File

@@ -0,0 +1,200 @@
/*++
/* NAME
/* base64_code 3
/* SUMMARY
/* encode/decode data, base 64 style
/* SYNOPSIS
/* #include <base64_code.h>
/*
/* VSTRING *base64_encode(result, in, len)
/* VSTRING *result;
/* const char *in;
/* int len;
/*
/* VSTRING *base64_decode(result, in, len)
/* VSTRING *result;
/* const char *in;
/* int len;
/* DESCRIPTION
/* base64_encode() takes a block of len bytes and encodes it as one
/* null-terminated string. The result value is the result argument.
/*
/* base64_decode() performs the opposite transformation. The result
/* value is the result argument. The result is null terminated, whether
/* or not that makes sense.
/* DIAGNOSTICS
/* base64_decode () returns a null pointer when the input contains
/* characters not in the base 64 alphabet.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include "sys_defs.h"
#include <ctype.h>
/* Utility library. */
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <base64_code.h>
/* Application-specific. */
static unsigned char to_b64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#define UNSIG_CHAR_PTR(x) ((unsigned char *)(x))
/* base64_encode - raw data to encoded */
VSTRING *base64_encode(VSTRING *result, const char *in, int len)
{
const unsigned char *cp;
int count;
/*
* Encode 3 -> 4.
*/
VSTRING_RESET(result);
for (cp = UNSIG_CHAR_PTR(in), count = len; count > 0; count -= 3, cp += 3) {
VSTRING_ADDCH(result, to_b64[cp[0] >> 2]);
if (count > 1) {
VSTRING_ADDCH(result, to_b64[(cp[0] & 0x3) << 4 | cp[1] >> 4]);
if (count > 2) {
VSTRING_ADDCH(result, to_b64[(cp[1] & 0xf) << 2 | cp[2] >> 6]);
VSTRING_ADDCH(result, to_b64[cp[2] & 0x3f]);
} else {
VSTRING_ADDCH(result, to_b64[(cp[1] & 0xf) << 2]);
VSTRING_ADDCH(result, '=');
break;
}
} else {
VSTRING_ADDCH(result, to_b64[(cp[0] & 0x3) << 4]);
VSTRING_ADDCH(result, '=');
VSTRING_ADDCH(result, '=');
break;
}
}
VSTRING_TERMINATE(result);
return (result);
}
/* base64_decode - encoded data to raw */
VSTRING *base64_decode(VSTRING *result, const char *in, int len)
{
static char *un_b64 = 0;
const unsigned char *cp;
int count;
int ch0;
int ch1;
int ch2;
int ch3;
#define CHARS_PER_BYTE 256
#define INVALID 0xff
/*
* Sanity check.
*/
if (len % 4)
return (0);
/*
* Once: initialize the decoding lookup table on the fly.
*/
if (un_b64 == 0) {
un_b64 = mymalloc(CHARS_PER_BYTE);
memset(un_b64, INVALID, CHARS_PER_BYTE);
for (cp = to_b64; cp < to_b64 + sizeof(to_b64); cp++)
un_b64[*cp] = cp - to_b64;
}
/*
* Decode 4 -> 3.
*/
VSTRING_RESET(result);
for (cp = UNSIG_CHAR_PTR(in), count = 0; count < len; count += 4) {
if ((ch0 = un_b64[*cp++]) == INVALID
|| (ch1 = un_b64[*cp++]) == INVALID)
return (0);
VSTRING_ADDCH(result, ch0 << 2 | ch1 >> 4);
if ((ch2 = *cp++) == '=')
break;
if ((ch2 = un_b64[ch2]) == INVALID)
return (0);
VSTRING_ADDCH(result, ch1 << 4 | ch2 >> 2);
if ((ch3 = *cp++) == '=')
break;
if ((ch3 = un_b64[ch3]) == INVALID)
return (0);
VSTRING_ADDCH(result, ch2 << 6 | ch3);
}
VSTRING_TERMINATE(result);
return (result);
}
#ifdef TEST
/*
* Proof-of-concept test program: convert to base 64 and back.
*/
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
int main(int unused_argc, char **unused_argv)
{
VSTRING *b1 = vstring_alloc(1);
VSTRING *b2 = vstring_alloc(1);
char *test = "this is a test";
#define DECODE(b,s,l) { \
if (base64_decode((b),(s),(l)) == 0) \
msg_panic("bad base64: %s", (s)); \
}
#define VERIFY(b,t) { \
if (strcmp((b), (t)) != 0) \
msg_panic("bad test: %s", (b)); \
}
base64_encode(b1, test, strlen(test));
DECODE(b2, STR(b1), LEN(b1));
VERIFY(STR(b2), test);
base64_encode(b1, test, strlen(test));
base64_encode(b2, STR(b1), LEN(b1));
base64_encode(b1, STR(b2), LEN(b2));
DECODE(b2, STR(b1), LEN(b1));
DECODE(b1, STR(b2), LEN(b2));
DECODE(b2, STR(b1), LEN(b1));
VERIFY(STR(b2), test);
base64_encode(b1, test, strlen(test));
base64_encode(b2, STR(b1), LEN(b1));
base64_encode(b1, STR(b2), LEN(b2));
base64_encode(b2, STR(b1), LEN(b1));
base64_encode(b1, STR(b2), LEN(b2));
DECODE(b2, STR(b1), LEN(b1));
DECODE(b1, STR(b2), LEN(b2));
DECODE(b2, STR(b1), LEN(b1));
DECODE(b1, STR(b2), LEN(b2));
DECODE(b2, STR(b1), LEN(b1));
VERIFY(STR(b2), test);
vstring_free(b1);
vstring_free(b2);
return (0);
}
#endif

View File

@@ -0,0 +1,36 @@
#ifndef _BASE64_CODE_H_INCLUDED_
#define _BASE64_CODE_H_INCLUDED_
/*++
/* NAME
/* base64_code 3h
/* SUMMARY
/* encode/decode data, base 64 style
/* SYNOPSIS
/* #include <base64_code.h>
/* DESCRIPTION
/* .nf
/*
* Utility library.
*/
#include <vstring.h>
/*
* External interface.
*/
extern VSTRING *base64_encode(VSTRING *, const char *, int);
extern VSTRING *base64_decode(VSTRING *, const char *, int);
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
#endif