Some systems allow you to inspect a running process with a system
call tracer. For example:
- # trace -p process-id
- # strace -p process-id
- # truss -p process-id
- # ktrace -p process-id
+ # trace -p process-id (SunOS 4)
+ # strace -p process-id (Linux and many others)
+ # truss -p process-id (Solaris, FreeBSD)
+ # ktrace -p process-id (generic 4.4BSD)
+
+Even more informative are traces of system library calls. Examples:
+
+ # ltrace -p process-id (Linux, also ported to FreeBSD and BSD/OS)
+ # sotruss -p process-id (Solaris)
See your system documentation for details.
wrong place. Problem tackled with help of Chip Christian.
Portability: reportedly, Solaris 2.5.1 can hang waiting
- for a UNIX-domain connection to be accepted, to it gets
+ for a UNIX-domain connection to be accepted, so it gets
the same workaround that was designed for LINUX. Problem
reported by Scott Cotton.
address masquerading. The default setting is backwards
compatible: envelope_sender header_sender header_recipient.
Files: cleanup/whatever.c.
+
+20010822
+
+ Code cleanup: the bounce daemon complained about data that
+ it was not going to send back anyway. Fix: stop reading
+ the original message when the bounce message reaches the
+ bounce message size limit. File: bounce/bounce_notify_util.c.
+
+20010826
+
+ Logging: postsuper now logs the queue ID when it requeues
+ a message, or when it deletes a message from the mail queue.
+ File: postsuper/postsuper.c.
+
+20010830
+
+ Safety: the SMTP server now sends a 4xx (try again later)
+ response when an UCE restriction is misconfigured, instead
+ of ignoring the bad restriction and possibly accepting mail
+ that it should not accept. File: smtpd/smtpd_check.c.
+
+20010907
+
+ Workaround: the Postfix qmqp-source program produced mail
+ not ending in newline that qmail-qmqpd accepts but that
+ qmail-remote was unable to deliver. Matthias Andree,
+ uni-dortmund.de. File: smtpstone/qmqp-source.c.
+
+20010910
+
+ Bugfix: smtp-sink broke when RCPT TO commands crossed a
+ network packet boundary. Problem reported by Matthias
+ Andree, uni-dortmund.de. File: smtpstone/smtp-sink.c.
+
+20010917
+
+ Code cleanup: permit_mx_backup implements the old behavior
+ (accept mail if the local MTA is MX relay), and allows an
+ additional restriction via the permit_mx_backup_networks
+ parameter (accept mail only if the primary MX hosts match
+ the specified list of network blocks). This second restriction
+ is now entirely optional, for backwards compatiblity.
+
+ Bugfix: an address extension could be appended multiple
+ times to the result of a canonical or virtual map lookup.
+ File: global/mail_addr_map.c. Fix by Victor Duchovni,
+ Morgan Stanley.
+
+ Bugfix: split_addr() would split an address even when there
+ was no data before the recipient delimiter. In combination
+ with the above bug, this could cause an address to grow
+ exponentially in size. Problem reported by Victor Duchovni,
+ Morgan Stanley. File: global/split_addr.c.
+
+20010918
+
+ Bugfix: the mail_addr_map() fix was almost but not quite
+ right. It took two really clever people and several iterations
+ of email to really fix the mail_addr_map() problem. Thanks
+ to Victor Duchovni and Liviu Daia.
+
+20011006
+
+ Cleanup: Postfix no longer flushes the whole deferred queue
+ after an ETRN request for a random domain name; the SMTP
+ server instead replies with "459 service unavailable".
+ Files: smtpd/smtpd.c, global/flush_clnt.c, flush/flush.c.
+
+Open problems:
+
+ Minor: The $process_id_directory setting is not used anywhere
+ in Postfix. Problem reported by Michael Smith, texas.net.
+
+ Medium: address rewriting should be configurable for envelopes
+ and headers.
LINUX PORTABILITY
=================
-On RedHat Linux 7.0, you must install the db3-devel RPM before you
+On RedHat Linux 7.0 you must install the db3-devel RPM before you
can compile the Postfix source code.
+On RedHat Linux 7.1 procmail no longer has permission to write the
+mail spool directory. Workaround: chmod 1777 /var/spool/mail.
+
LINUX SYSLOGD PERFORMANCE
=========================
-LINUX syslogd uses synchronous writes by default, which is very
-expensive. For services such as mail it is recommended that you
-disable synchronous logfile writes by editing /etc/syslog.conf and
-by prepending a - to the logfile name:
+LINUX syslogd uses synchronous writes by default. Because of this,
+syslogd can actually use more system resources than Postfix. To
+avoid such madness, disable synchronous mail logfile writes by
+editing /etc/syslog.conf and by prepending a - to the logfile name:
mail.* -/var/log/mail.log
+Incompatible changes with snapshot-20011007
+===========================================
+
+Postfix no longer flushes the whole queue after an ETRN request
+for a random domain name. Requests for random domain names are now
+rejected instead.
+
+The permit_mx_backup feature has changed. It accepts mail when the
+local machine is listed in the DNS as MX relay host for the given
+destination. The optional permit_mx_backup_networks parameter can
+further require that the primary MX hosts for the given destinations
+match specific network blocks. This optional restriction is off by
+default.
+
Incompatible changes with snapshot-20010808
===========================================
when the primary MX hosts for the recipient match the networks that
are specified with the new auth_mx_backup_networks configuration
parameter. Postfix refuses to accept mail when permit_mx_backup
-is used while auth_mx_backup_networks is not configured.
+is used while auth_mx_backup_networks is not configured. [This
+change was undone with a later release].
The protocol between Postfix master and child processes has changed.
You must stop and start Postfix in order to switch between Snapshot
# gateway, you should also include $mydomain. Do not specify the
# names of domains that this machine is backup MX host for. Specify
# those names via the relay_domains or permit_mx_backup settings for
-# the SMTP server (see sample-smtpd.cf.
+# the SMTP server (see sample-smtpd.cf).
#
# The local machine is always the final destination for mail addressed
# to user@[the.net.work.address] of an interface that the mail system
# gateway, you should also include $mydomain. Do not specify the
# names of domains that this machine is backup MX host for. Specify
# those names via the relay_domains or permit_mx_backup settings for
-# the SMTP server (see sample-smtpd.cf.
+# the SMTP server (see sample-smtpd.cf).
#
# The local machine is always the final destination for mail addressed
# to user@[the.net.work.address] of an interface that the mail system
# - to destinations matching $relay_domains or subdomain thereof,
# except for addresses with sender-specified routing.
# reject_unauth_pipelining: reject mail from improperly pipelining spamware
-# permit_mx_backup: accept mail for sites whose primary MX hosts
-# match the networks specified with auth_mx_backup_networks.
+# permit_mx_backup: accept mail for sites that list me as MX host.
+# Use the optional permit_mx_backup_networks parameter to also
+# require that the primary MX hosts match a list of network blocks.
# reject_unknown_recipient_domain: reject domains without A or MX record.
# check_recipient_access maptype:mapname
# maptype:mapname: look up recipient address, parent domain, or localpart@.
#
smtpd_recipient_restrictions = permit_mynetworks,check_relay_domains
-# The auth_mx_backup_networks parameter specifies a list of networks
-# for which the permit_mx_backup feature (see above) can be used.
-#
-# By default, auth_mx_backup_networks is empty and no networks are
-# authorized to use the permit_mx_backup feature. You can specify
-# a complete class A network (X.0.0.0/8), a complete class B network
-# (X.X.0.0/16), and so on. If you want stricter control, specify a
-# list of network/mask patterns, where the mask specifies the number
-# of bits in the network part of a host address. You can also specify
-# the absolute pathname of a pattern file instead of listing the
-# patterns here.
-#
-auth_mx_backup_networks =
-
#
# ADDITIONAL UCE CONTROLS
#
<p>
<li><a href="http://www.inter7.com/courierimap/">Courier-Imap</a>
-provides POP3, IMAP, POP3 and IMAP, and supports access over SSL.
+provides POP3 and IMAP, and supports access over SSL.
This software supports the maildir-style mailbox format only
(one message per file, same format as qmail).
<li> <a href="uce.html#permit_mx_backup">permit_mx_backup</a>:
permit if the local system is listed as MX host for the recipient
-domain, provided that the primary MX host for the recipient domain
-is within the networks specified with <a
-href="uce.html#auth_mx_backup_networks">auth_mx_backup_networks</a>.
+domain. Use the optional <a
+href="#permit_mx_backup_networks">permit_mx_backup_networks</a>
+parameter to also require that the primary MX hosts match a list
+of network blocks.
<li> Other UCE restrictions (e.g., SMTPD access maps) are not aware
of sender-provided routing information.
undeliverable it will be added back to the logfile.
If the destination is not eligible for a fast flush
- logfile, this request triggers delivery of all
- queued mail.
+ logfile, this request is rejected (see below for
+ status codes).
<b>TRIGGER</b><i>_</i><b>REQ</b><i>_</i><b>WAKEUP</b>
This wakeup request from the master is an alterna-
<b>FLUSH</b><i>_</i><b>STAT</b><i>_</i><b>FAIL</b>
The request failed.
+ <b>FLUSH</b><i>_</i><b>STAT</b><i>_</i><b>DENY</b>
+ The request was denied because the destination
+ domain is not eligible for fast flush service, or
+ because the fast flush service is disabled.
+
<b>SECURITY</b>
The fast flush server is not security-sensitive. It does
not talk to the network, and it does not talk to local
List of DNS domains that publish the addresses of
blacklisted hosts.
+ <b>permit</b><i>_</i><b>mx</b><i>_</i><b>backup</b><i>_</i><b>networks</b>
+ Only domains whose primary MX hosts match the
+ listed networks are eligible for the <b>per-</b>
+ <b>mit</b><i>_</i><b>mx</b><i>_</i><b>backup</b> feature.
+
<b>relay</b><i>_</i><b>domains</b>
Restrict what domains or networks this mail system
will relay mail from or to.
<p>
+By default, this restriction is applied when the client sends the
+RCPT TO command. In order to have the restriction take effect
+as soon as possible, specify <b>smtpd_delay_reject = yes</b> in
+the Postfix <b>main.cf</b> configuration file. Doing so may cause
+unexpected results with poorly implemented client software.
+
+<p>
+
<dl>
<dt>Default:
clients may send with the <b>HELO</b> (<b>EHLO</b>) command. Some
UCE software can be stopped by being strict here.
+<p>
+
+By default, this restriction is applied when the client sends the
+RCPT TO command. In order to have the restriction take effect
+as soon as possible, specify <b>smtpd_delay_reject = yes</b> in
+the Postfix <b>main.cf</b> configuration file. Doing so may cause
+unexpected results with poorly implemented client software.
+
<dl>
<dt>Default:
<p>
+By default, this restriction is applied when the client sends the
+RCPT TO command. In order to have the restriction take effect
+as soon as possible, specify <b>smtpd_delay_reject = yes</b> in
+the Postfix <b>main.cf</b> configuration file. Doing so may cause
+unexpected results with poorly implemented client software.
+
+<p>
+
<dl>
<dt> Default:
<a name="permit_mx_backup">
<dt> <b>permit_mx_backup</b> <dd> Permit the request when the local
-mail system is MX host for the resolved destination, provided that
-the primary MX host is within the networks specified with <a
-href="#auth_mx_backup_networks">auth_mx_backup_networks</a> parameter.
+mail system is MX host for the resolved destination.
This includes the case that the local mail system is the final
destination. However, the SMTP server will not forward mail with
addresses that have sender-specified routing information (example:
<p>
+Use the optional <a href="#permit_mx_backup_networks">
+permit_mx_backup_networks</a> parameter to also require that the
+primary MX hosts match a list of network blocks.
+
+<p>
+
Relevant configuration parameters: <a
-href="#auth_mx_backup_networks">auth_mx_backup_networks</a>, <a
-href="basic.html#mydestination"> $mydestination</a>, <a
+href="#permit_mx_backup_networks">permit_mx_backup_networks</a>,
+<a href="basic.html#mydestination"> $mydestination</a>, <a
href="basic.html#inet_interfaces"> $inet_interfaces</a>.
<p>
<dl>
-<a name="auth_mx_backup_networks">
+<a name="permit_mx_backup_networks">
-<dt> <b>auth_mx_backup_networks</b>
+<dt> <b>permit_mx_backup_networks</b>
-<dd>This parameter specifies the networks that are allowed to
-use the <a href="#permit_mx_backup">permit_mx_backup</a>
-relay control feature.
+<dd>Restrict the use of the <a href="#permit_mx_backup">
+permit_mx_backup</a> relay control feature to destinations whose
+primary MX hosts match a list of network blocks.
<p>
<dt>Default:
-<dd><b>auth_mx_backup_networks = </b>
+<dd><b>permit_mx_backup_networks = </b>
<p>
-That is, no networks are authorized by default.
+That is, all networks are authorized by default.
<p>
<dl>
-<dd> <b>auth_mx_backup_networks = 168.100.0.0/16</b>
+<dd> <b>permit_mx_backup_networks = 168.100.0.0/16</b>
</dl>
;;
OpenBSD.2*) SYSTYPE=OPENBSD2
;;
+ OpenBSD.3*) SYSTYPE=OPENBSD3
+ ;;
NetBSD.1*) SYSTYPE=NETBSD1
;;
BSD/OS.2*) SYSTYPE=BSDI2
if mail is undeliverable it will be added back to the logfile.
.sp
If the destination is not eligible for a fast flush logfile,
-this request triggers delivery of all queued mail.
+this request is rejected (see below for status codes).
.IP \fBTRIGGER_REQ_WAKEUP\fR
This wakeup request from the master is an alternative way to
request \fBFLUSH_REQ_REFRESH\fR.
request parameter value).
.IP \fBFLUSH_STAT_FAIL\fR
The request failed.
+.IP \fBFLUSH_STAT_DENY\fR
+The request was denied because the destination domain is not
+eligible for fast flush service, or because the fast flush
+service is disabled.
.SH SECURITY
.na
.nf
.IP \fBmaps_rbl_domains\fR
List of DNS domains that publish the addresses of blacklisted
hosts.
+.IP \fBpermit_mx_backup_networks\fR
+Only domains whose primary MX hosts match the listed networks
+are eligible for the \fBpermit_mx_backup\fR feature.
.IP \fBrelay_domains\fR
Restrict what domains or networks this mail system will relay
mail from or to.
"####################################################################");
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce,
- "Your message could not be delivered for %.1f hours.",
+ "Your message could not be delivered for %.1f hours.",
var_delay_warn_time / 3600.0);
post_mail_fprintf(bounce,
"It will be retried until it is %.1f days old.",
#endif
if (bounce_info->flush == 0)
post_mail_fprintf(bounce, "Will-Retry-Until: %s",
- mail_date(bounce_info->arrival_time + var_max_queue_time));
+ mail_date(bounce_info->arrival_time + var_max_queue_time));
return (vstream_ferror(bounce));
}
if (var_bounce_limit == 0 || bounce_length < var_bounce_limit) {
bounce_length += VSTRING_LEN(bounce_info->buf) + 2;
status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type);
- }
+ } else
+ break;
}
/*
* Final MIME headers. These require -- at the end of the boundary
* string.
+ *
+ * XXX This should be a separate bounce_terminate() entry so we can be
+ * assured that the terminator will always be sent.
*/
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "--%s--", bounce_info->mime_boundary);
cleanup.o: ../../include/mail_params.h
cleanup.o: ../../include/record.h
cleanup.o: ../../include/rec_type.h
-cleanup.o: ../../include/mail_flow.h
cleanup.o: ../../include/mail_server.h
cleanup.o: cleanup.h
cleanup.o: ../../include/maps.h
cleanup_init.o: ../../include/sys_defs.h
cleanup_init.o: ../../include/msg.h
cleanup_init.o: ../../include/iostuff.h
+cleanup_init.o: ../../include/name_mask.h
cleanup_init.o: ../../include/mail_addr.h
cleanup_init.o: ../../include/mail_params.h
cleanup_init.o: ../../include/ext_prop.h
/* if mail is undeliverable it will be added back to the logfile.
/* .sp
/* If the destination is not eligible for a fast flush logfile,
-/* this request triggers delivery of all queued mail.
+/* this request is rejected (see below for status codes).
/* .IP \fBTRIGGER_REQ_WAKEUP\fR
/* This wakeup request from the master is an alternative way to
/* request \fBFLUSH_REQ_REFRESH\fR.
/* request parameter value).
/* .IP \fBFLUSH_STAT_FAIL\fR
/* The request failed.
+/* .IP \fBFLUSH_STAT_DENY\fR
+/* The request was denied because the destination domain is not
+/* eligible for fast flush service, or because the fast flush
+/* service is disabled.
/* SECURITY
/* .ad
/* .fi
msg_info("%s: site %s queue_id %s", myname, site, queue_id);
/*
- * If this site is not eligible for logging, just ignore the request.
+ * If this site is not eligible for logging, deny the request.
*/
if (flush_policy_ok(site) == 0)
- return (FLUSH_STAT_OK);
+ return (FLUSH_STAT_DENY);
/*
* Map site to path and update log.
msg_info("%s: site %s", myname, site);
/*
- * If this site is not eligible for logging, deliver all queued mail.
+ * If this site is not eligible for logging, deny the request.
*/
if (flush_policy_ok(site) == 0)
- return (mail_flush_deferred());
+ return (FLUSH_STAT_DENY);
/*
* Map site name to path name and flush the log.
* bounce/defer daemon? Well, doing it here is more robust.
*/
if ((rcpt_domain = strrchr(recipient, '@')) != 0 && *++rcpt_domain != 0)
- if (flush_add(rcpt_domain, id) != FLUSH_STAT_OK)
+ switch (flush_add(rcpt_domain, id)) {
+ case FLUSH_STAT_OK:
+ case FLUSH_STAT_DENY:
+ break;
+ default:
msg_warn("unable to talk to fast flush service");
+ break;
+ }
return (-1);
}
/* .IP MAIL_FLUSH_BAD
/* The "fast flush" server rejected the request (invalid request
/* parameter).
+/* .IP MAIL_FLUSH_DENY
+/* The specified domain is not eligible for "fast flush" service.
/* SEE ALSO
/* flush(8) Postfix fast flush cache manager
/* LICENSE
* Don't bother the server if the service is turned off.
*/
if (*var_fflush_domains == 0)
- status = FLUSH_STAT_OK;
+ status = FLUSH_STAT_DENY;
else
status = mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH,
"%s", FLUSH_REQ_PURGE);
* Don't bother the server if the service is turned off.
*/
if (*var_fflush_domains == 0)
- status = FLUSH_STAT_OK;
+ status = FLUSH_STAT_DENY;
else
status = mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH,
"%s", FLUSH_REQ_REFRESH);
* Don't bother the server if the service is turned off.
*/
if (*var_fflush_domains == 0)
- status = mail_flush_deferred();
+ status = FLUSH_STAT_DENY;
else
status = mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH,
"%s %s", FLUSH_REQ_SEND, site);
* Don't bother the server if the service is turned off.
*/
if (*var_fflush_domains == 0)
- status = FLUSH_STAT_OK;
+ status = FLUSH_STAT_DENY;
else
status = mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH,
"%s %s %s", FLUSH_REQ_ADD, site, queue_id);
#define FLUSH_STAT_FAIL -1 /* request failed */
#define FLUSH_STAT_OK 0 /* request executed */
#define FLUSH_STAT_BAD 3 /* invalid parameter */
+#define FLUSH_STAT_DENY 4 /* request denied */
/* LICENSE
/* Application-specific. */
#define STR vstring_str
+#define LEN VSTRING_LEN
/* mail_addr_map - map a canonical address */
if ((string = mail_addr_find(path, address, &extension)) != 0) {
/*
- * Prepend the original user to @otherdomain.
+ * Prepend the original user to @otherdomain, but do not propagate
+ * the unmatched address extension.
*/
if (*string == '@') {
buffer = vstring_alloc(100);
vstring_strncpy(buffer, address, ratsign - address);
else
vstring_strcpy(buffer, address);
+ if (extension)
+ vstring_truncate(buffer, LEN(buffer) - strlen(extension));
vstring_strcat(buffer, string);
string = STR(buffer);
}
msg_fatal("chdir %s: %m", var_queue_dir);
path = maps_create(argv[0], argv[1], DICT_FLAG_LOCK);
while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
+ msg_info("=== Address extension on, extension propagation on ===");
+ var_rcpt_delim = "+";
+ if ((result = mail_addr_map(path, STR(buffer), 1)) != 0)
+ argv_free(result);
+ msg_info("=== Address extension on, extension propagation off ===");
+ if ((result = mail_addr_map(path, STR(buffer), 0)) != 0)
+ argv_free(result);
+ msg_info("=== Address extension off ===");
+ var_rcpt_delim = "";
if ((result = mail_addr_map(path, STR(buffer), 1)) != 0)
argv_free(result);
}
#define PERMIT_MX_BACKUP "permit_mx_backup"
-#define VAR_AUTH_MX_NETWORKS "auth_mx_backup_networks"
-#define DEF_AUTH_MX_NETWORKS ""
-extern char *var_auth_mx_networks;
+#define VAR_PERM_MX_NETWORKS "permit_mx_backup_networks"
+#define DEF_PERM_MX_NETWORKS ""
+extern char *var_perm_mx_networks;
#define VAR_ACCESS_MAP_CODE "access_map_reject_code"
#define DEF_ACCESS_MAP_CODE 554
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20010808"
+#define DEF_MAIL_VERSION "Snapshot-20011006"
extern char *var_mail_version;
/* LICENSE
}
/*
- * Safe to split this address.
+ * Safe to split this address. Do not split the address if the result
+ * would have a null localpart.
*/
- return (split_at(localpart, delimiter));
+ return (delimiter == *localpart ? 0 : split_at(localpart, delimiter));
}
qmgr.o: ../../include/mail_params.h
qmgr.o: ../../include/mail_proto.h
qmgr.o: ../../include/iostuff.h
+qmgr.o: ../../include/mail_flow.h
qmgr.o: ../../include/master_proto.h
qmgr.o: ../../include/mail_server.h
qmgr.o: qmgr.h
postremove(mail_queue_path(log_path_buf, *log_qpp, queue_id));
if (postremove(msg_path) == 0) {
found = 1;
+ msg_info("%s: removed", queue_id);
break;
} /* else: maybe lost a race */
}
continue;
(void) mail_queue_path(new_path_buf, MAIL_QUEUE_MAILDROP, queue_id);
if (postrename(old_path, STR(new_path_buf)) == 0) {
+ msg_info("%s: requeued", queue_id);
found = 1;
break;
} /* else: maybe lost a race */
qmgr.o: ../../include/mail_params.h
qmgr.o: ../../include/mail_proto.h
qmgr.o: ../../include/iostuff.h
+qmgr.o: ../../include/mail_flow.h
qmgr.o: ../../include/master_proto.h
qmgr.o: ../../include/mail_server.h
qmgr.o: qmgr.h
/* .IP \fBmaps_rbl_domains\fR
/* List of DNS domains that publish the addresses of blacklisted
/* hosts.
+/* .IP \fBpermit_mx_backup_networks\fR
+/* Only domains whose primary MX hosts match the listed networks
+/* are eligible for the \fBpermit_mx_backup\fR feature.
/* .IP \fBrelay_domains\fR
/* Restrict what domains or networks this mail system will relay
/* mail from or to.
char *var_smtpd_sasl_realm;
char *var_filter_xport;
bool var_broken_auth_clients;
-char *var_auth_mx_networks;
+char *var_perm_mx_networks;
/*
* Global state, for stand-alone mode queue file cleanup. When this is
case FLUSH_STAT_OK:
smtpd_chat_reply(state, "250 Queuing started");
return (0);
+ case FLUSH_STAT_DENY:
+ msg_warn("reject: ETRN %.100s... from %s",
+ argv[1].strval, state->namaddr);
+ smtpd_chat_reply(state, "459 <%s>: service unavailable",
+ argv[1].strval);
+ return (-1);
case FLUSH_STAT_BAD:
msg_warn("bad ETRN %.100s... from %s", argv[1].strval, state->namaddr);
smtpd_chat_reply(state, "458 Unable to queue messages");
VAR_SMTPD_SASL_OPTS, DEF_SMTPD_SASL_OPTS, &var_smtpd_sasl_opts, 0, 0,
VAR_SMTPD_SASL_REALM, DEF_SMTPD_SASL_REALM, &var_smtpd_sasl_realm, 1, 0,
VAR_FILTER_XPORT, DEF_FILTER_XPORT, &var_filter_xport, 0, 0,
- VAR_AUTH_MX_NETWORKS, DEF_AUTH_MX_NETWORKS, &var_auth_mx_networks, 0, 0,
+ VAR_PERM_MX_NETWORKS, DEF_PERM_MX_NETWORKS, &var_perm_mx_networks, 0, 0,
0,
};
*/
static DOMAIN_LIST *relay_domains;
static NAMADR_LIST *mynetworks;
-static NAMADR_LIST *auth_mx_networks;
+static NAMADR_LIST *perm_mx_networks;
/*
* Pre-parsed restriction lists.
*/
mynetworks = namadr_list_init(var_mynetworks);
relay_domains = domain_list_init(var_relay_domains);
- auth_mx_networks = namadr_list_init(var_auth_mx_networks);
+ perm_mx_networks = namadr_list_init(var_perm_mx_networks);
/*
* Pre-parse and pre-open the recipient maps.
if (msg_verbose)
msg_info("%s: checking: %s", myname, inet_ntoa(addr));
- if (!namadr_list_match(auth_mx_networks, host, inet_ntoa(addr))) {
+ if (!namadr_list_match(perm_mx_networks, host, inet_ntoa(addr))) {
/*
* Reject: at least one IP address is not listed in
*/
if (msg_verbose)
msg_info("%s: address %s does not match %s",
- myname, inet_ntoa(addr), VAR_AUTH_MX_NETWORKS);
+ myname, inet_ntoa(addr), VAR_PERM_MX_NETWORKS);
dns_rr_free(addr_list);
return (NOPE);
}
return (NOPE);
}
-#if 0
-
-/* permit_mx_backup - permit use of me as MX backup for recipient domain */
+/* i_am_mx - is this machine listed as MX relay */
-static int permit_mx_backup(SMTPD_STATE *state, const char *recipient)
+static int i_am_mx(DNS_RR *mx_list)
{
- char *myname = "permit_mx_backup";
- const RESOLVE_REPLY *reply;
- const char *domain;
-
- DNS_RR *mx_list;
+ const char *myname = "permit_mx_backup";
DNS_RR *mx;
- int dns_status;
-
- if (msg_verbose)
- msg_info("%s: %s", myname, recipient);
/*
- * Resolve the address.
+ * Compare hostnames first. Only if no name match is found, go through
+ * the trouble of host address lookups.
*/
- reply = (const RESOLVE_REPLY *)
- ctable_locate(smtpd_resolve_cache, recipient);
+ for (mx = mx_list; mx != 0; mx = mx->next) {
+ if (msg_verbose)
+ msg_info("%s: resolve hostname: %s", myname, (char *) mx->data);
+ if (resolve_local((char *) mx->data))
+ return (YUP);
+ }
/*
- * If the destination is local, it is acceptable, because we are
- * supposedly MX for our own address.
+ * Argh. Do further DNS lookups and match interface addresses.
*/
- if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0)
- return (SMTPD_CHECK_OK);
- domain += 1;
- if (resolve_local(domain)
- || (*var_virtual_maps
- && check_maps_find(state, recipient, virtual_maps, domain, 0))
- || (*var_virt_mailbox_maps
- && check_maps_find(state, recipient, virt_mailbox_maps, domain, 0)))
- return (SMTPD_CHECK_OK);
-
- if (msg_verbose)
- msg_info("%s: not local: %s", myname, recipient);
+ for (mx = mx_list; mx != 0; mx = mx->next) {
+ if (msg_verbose)
+ msg_info("%s: address lookup: %s", myname, (char *) mx->data);
+ if (has_my_addr((char *) mx->data))
+ return (YUP);
+ }
/*
- * Skip source-routed mail (uncertain destination).
+ * This machine is not listed as MX relay.
*/
- if (var_allow_untrust_route == 0 && (reply->flags & RESOLVE_FLAG_ROUTED))
- return (SMTPD_CHECK_DUNNO);
+ if (msg_verbose)
+ msg_info("%s: I am not listed as MX relay", myname);
+ return (NOPE);
+}
- /*
- * Skip numerical forms that didn't match the local system.
- */
- if (domain[0] == '#'
- || (domain[0] == '[' && domain[strlen(domain) - 1] == ']'))
- return (SMTPD_CHECK_DUNNO);
+/* permit_mx_primary - authorize primary MX relays */
+
+static int permit_mx_primary(DNS_RR *mx_list)
+{
+ DNS_RR *mx;
+ int best_pref;
+ int status;
/*
- * Look up the list of MX host names for this domain. If no MX host is
- * found, perhaps it is a CNAME for the local machine. Clients aren't
- * supposed to send CNAMEs in SMTP commands, but it happens anyway. If we
- * can't look up the destination, play safe and assume it is OK.
+ * Find the preference of the primary MX hosts.
*/
- dns_status = dns_lookup(domain, T_MX, 0, &mx_list,
- (VSTRING *) 0, (VSTRING *) 0);
- if (dns_status == DNS_NOTFOUND)
- return (has_my_addr(domain) ? SMTPD_CHECK_OK : SMTPD_CHECK_DUNNO);
- if (dns_status != DNS_OK)
- return (SMTPD_CHECK_TRYAGAIN);
+ for (best_pref = 0xffff, mx = mx_list; mx != 0; mx = mx->next)
+ if (mx->pref < best_pref)
+ best_pref = mx->pref;
/*
- * First, see if we match any of the MX host names listed. Only if no
- * name match is found, go through the trouble of host address lookups.
+ * See if each best MX host has all IP addresses in
+ * permit_mx_backup_networks.
*/
for (mx = mx_list; mx != 0; mx = mx->next) {
- if (msg_verbose)
- msg_info("%s: resolve hostname: %s", myname, (char *) mx->data);
- if (resolve_local((char *) mx->data)) {
- dns_rr_free(mx_list);
- return (SMTPD_CHECK_OK);
+ if (mx->pref != best_pref)
+ continue;
+ switch (status = all_auth_mx_addr((char *) mx->data)) {
+ case TRYAGAIN:
+ case NOPE:
+ return (status);
}
}
/*
- * Argh. Do further DNS lookups and match interface addresses.
+ * All IP addresses of the best MX hosts are within
+ * auth_mx_backup_networks.
*/
- for (mx = mx_list; mx != 0; mx = mx->next) {
- if (msg_verbose)
- msg_info("%s: address lookup: %s", myname, (char *) mx->data);
- if (has_my_addr((char *) mx->data)) {
- dns_rr_free(mx_list);
- return (SMTPD_CHECK_OK);
- }
- }
- if (msg_verbose)
- msg_info("%s: no match", myname);
-
- dns_rr_free(mx_list);
- return (SMTPD_CHECK_DUNNO);
+ return (YUP);
}
-#endif
-
-/* permit_auth_mx_backup - relay for authorized networks */
+/* permit_mx_backup - permit use of me as MX backup for recipient domain */
-static int permit_auth_mx_backup(SMTPD_STATE *state, const char *recipient)
+static int permit_mx_backup(SMTPD_STATE *state, const char *recipient)
{
- char *myname = "permit_auth_mx_backup";
+ char *myname = "permit_mx_backup";
const RESOLVE_REPLY *reply;
const char *domain;
DNS_RR *mx_list;
- DNS_RR *mx;
int dns_status;
- int best_pref;
if (msg_verbose)
msg_info("%s: %s", myname, recipient);
- /*
- * Sanity check.
- */
- if (*var_auth_mx_networks == 0) {
- msg_warn("The %s feature requires that you specify authorized networks",
- PERMIT_MX_BACKUP);
- msg_warn("via the %s configuration parameter. See examples",
- VAR_AUTH_MX_NETWORKS);
- msg_warn("in the %s/sample-smtpd.cf configuration file.",
- var_config_dir);
- longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
- "%d <%s>: Configuration error in %s",
- 451, recipient,
- VAR_AUTH_MX_NETWORKS));
- }
-
/*
* Resolve the address.
*/
/*
* Look up the list of MX host names for this domain. If no MX host is
* found, perhaps it is a CNAME for the local machine. Clients aren't
- * supposed to send CNAMEs in SMTP commands, but it happens anyway.
+ * supposed to send CNAMEs in SMTP commands, but it happens anyway. If we
+ * can't look up the destination, play safe and assume it is OK.
*/
dns_status = dns_lookup(domain, T_MX, 0, &mx_list,
(VSTRING *) 0, (VSTRING *) 0);
return (SMTPD_CHECK_TRYAGAIN);
/*
- * Find the preference of the primary MX hosts.
+ * First, see if we match any of the MX host names listed.
*/
- for (best_pref = 0xffff, mx = mx_list; mx != 0; mx = mx->next)
- if (mx->pref < best_pref)
- best_pref = mx->pref;
+ if (!i_am_mx(mx_list)) {
+ dns_rr_free(mx_list);
+ return (SMTPD_CHECK_DUNNO);
+ }
/*
- * See if each best MX host has all IP addresses in
- * auth_mx_backup_networks.
+ * Optionally, see if the primary MX hosts are in a restricted list of
+ * networks.
*/
- for (mx = mx_list; mx != 0; mx = mx->next) {
- if (mx->pref != best_pref)
- continue;
- switch (all_auth_mx_addr((char *) mx->data)) {
- case NOPE:
- dns_rr_free(mx_list);
- return (SMTPD_CHECK_DUNNO);
- case YUP:
- continue;
- case TRYAGAIN:
- dns_rr_free(mx_list);
- return (SMTPD_CHECK_TRYAGAIN);
- }
+ if (*var_perm_mx_networks && !permit_mx_primary(mx_list)) {
+ dns_rr_free(mx_list);
+ return (SMTPD_CHECK_DUNNO);
}
/*
- * All IP addresses of the best MX hosts are within
- * auth_mx_backup_networks.
+ * The destination passed all requirements.
*/
dns_rr_free(mx_list);
return (SMTPD_CHECK_OK);
table, value);
msg_warn("do not specify lookup tables inside SMTPD access maps");
msg_warn("define a restriction class and specify its name instead");
- longjmp(smtpd_check_buf, -1);
+ longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
+ "451 Server configuration error"));
}
/*
if (state->recursion++ > 100) {
msg_warn("SMTPD access map %s entry %s causes unreasonable recursion",
table, value);
- longjmp(smtpd_check_buf, -1);
+ longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
+ "451 Server configuration error"));
}
/*
/* is_map_command - restriction has form: check_xxx_access type:name */
-static int is_map_command(const char *name, const char *command, char ***argp)
+static int is_map_command(SMTPD_STATE *state, const char *name,
+ const char *command, char ***argp)
{
/*
return (0);
} else if (*(*argp + 1) == 0 || strchr(*(*argp += 1), ':') == 0) {
msg_warn("restriction %s requires maptype:mapname", command);
- longjmp(smtpd_check_buf, -1);
+ longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
+ "451 Server configuration error"));
} else {
return (1);
}
status = reject_unknown_client(state);
} else if (strcasecmp(name, PERMIT_MYNETWORKS) == 0) {
status = permit_mynetworks(state);
- } else if (is_map_command(name, CHECK_CLIENT_ACL, &cpp)) {
+ } else if (is_map_command(state, name, CHECK_CLIENT_ACL, &cpp)) {
status = check_namadr_access(state, *cpp, state->name, state->addr,
FULL, &found, state->namaddr,
SMTPD_NAME_CLIENT, def_acl);
/*
* HELO/EHLO parameter restrictions.
*/
- else if (is_map_command(name, CHECK_HELO_ACL, &cpp)) {
+ else if (is_map_command(state, name, CHECK_HELO_ACL, &cpp)) {
if (state->helo_name)
status = check_domain_access(state, *cpp, state->helo_name,
FULL, &found, state->helo_name,
/*
* Sender mail address restrictions.
*/
- else if (is_map_command(name, CHECK_SENDER_ACL, &cpp)) {
+ else if (is_map_command(state, name, CHECK_SENDER_ACL, &cpp)) {
if (state->sender && *state->sender)
status = check_mail_access(state, *cpp, state->sender,
&found, state->sender,
/*
* Recipient mail address restrictions.
*/
- else if (is_map_command(name, CHECK_RECIP_ACL, &cpp)) {
+ else if (is_map_command(state, name, CHECK_RECIP_ACL, &cpp)) {
if (state->recipient)
status = check_mail_access(state, *cpp, state->recipient,
&found, state->recipient,
SMTPD_NAME_RECIPIENT, def_acl);
} else if (strcasecmp(name, PERMIT_MX_BACKUP) == 0) {
if (state->recipient)
- status = permit_auth_mx_backup(state, state->recipient);
+ status = permit_mx_backup(state, state->recipient);
} else if (strcasecmp(name, PERMIT_AUTH_DEST) == 0) {
if (state->recipient)
status = permit_auth_destination(state, state->recipient);
/*
* ETRN domain name restrictions.
*/
- else if (is_map_command(name, CHECK_ETRN_ACL, &cpp)) {
+ else if (is_map_command(state, name, CHECK_ETRN_ACL, &cpp)) {
if (state->etrn_name)
status = check_domain_access(state, *cpp, state->etrn_name,
FULL, &found, state->etrn_name,
*/
else {
msg_warn("unknown smtpd restriction: \"%s\"", name);
- break;
+ longjmp(smtpd_check_buf, smtpd_check_reject(state,
+ MAIL_ERROR_SOFTWARE, "451 Server configuration error"));
}
if (msg_verbose)
msg_info("%s: name=%s status=%d", myname, name, status);
VSTRING_ADDCH(buffer, 'X');
VSTRING_ADDCH(buffer, '\n');
}
+ STR(buffer)[message_length - 1] = '\n';
netstring_memcpy(message_buffer, STR(buffer), message_length);
n = strlen(sender);
typedef struct SINK_STATE {
VSTREAM *stream;
+ VSTRING *buffer;
int data_state;
int (*read) (struct SINK_STATE *);
int rcpts;
#define ST_CR_LF_DOT_CR_LF 5
static int var_tmout;
-static int var_max_line_length;
+static int var_max_line_length = 2048;
static char *var_myhostname;
-static VSTRING *buffer;
static int command_read(SINK_STATE *);
static int data_read(SINK_STATE *);
static void disconnect(SINK_STATE *);
struct data_trans *dp;
/*
- * We must avoid blocking I/O, so get out of here as soon as both the
- * VSTREAM and kernel read buffers dry up.
+ * A read may result in EOF, but is never supposed to time out - a time
+ * out means that we were trying to read when no data was available.
*/
- while (vstream_peek(state->stream) > 0
- || peekfd(vstream_fileno(state->stream)) > 0) {
+ for (;;) {
if ((ch = VSTREAM_GETC(state->stream)) == VSTREAM_EOF)
return (-1);
for (dp = data_trans; dp->state != state->data_state; dp++)
msg_info(".");
dot_response(state);
state->read = command_read;
+ state->data_state = ST_ANY;
break;
}
+
+ /*
+ * We must avoid blocking I/O, so get out of here as soon as both the
+ * VSTREAM and kernel read buffers dry up.
+ */
+ if (vstream_peek(state->stream) <= 0
+ && peekfd(vstream_fileno(state->stream)) <= 0)
+ return (0);
}
return (0);
}
{
char *command;
SINK_COMMAND *cmdp;
+ int ch;
+ struct cmd_trans {
+ int state;
+ int want;
+ int next_state;
+ };
+ static struct cmd_trans cmd_trans[] = {
+ ST_ANY, '\r', ST_CR,
+ ST_CR, '\n', ST_CR_LF,
+ };
+ struct cmd_trans *cp;
+
+ /*
+ * A read may result in EOF, but is never supposed to time out - a time
+ * out means that we were trying to read when no data was available.
+ */
+ for (;;) {
+ if ((ch = VSTREAM_GETC(state->stream)) == VSTREAM_EOF)
+ return (-1);
+
+ /*
+ * Sanity check. We don't want to store infinitely long commands.
+ */
+ if (VSTRING_LEN(state->buffer) >= var_max_line_length) {
+ msg_warn("command line too long");
+ return (-1);
+ }
+ VSTRING_ADDCH(state->buffer, ch);
+
+ /*
+ * Try to match the current character desired by the state machine.
+ * If that fails, try to restart the machine with a match for its
+ * first state.
+ */
+ for (cp = cmd_trans; cp->state != state->data_state; cp++)
+ /* void */ ;
+ if (ch == cp->want)
+ state->data_state = cp->next_state;
+ else if (ch == cmd_trans[0].want)
+ state->data_state = cmd_trans[0].next_state;
+ else
+ state->data_state = ST_ANY;
+ if (state->data_state == ST_CR_LF)
+ break;
+
+ /*
+ * We must avoid blocking I/O, so get out of here as soon as both the
+ * VSTREAM and kernel read buffers dry up.
+ */
+ if (vstream_peek(state->stream) <= 0
+ && peekfd(vstream_fileno(state->stream)) <= 0)
+ return (0);
+ }
- smtp_get(buffer, state->stream, var_max_line_length);
- if ((command = strtok(vstring_str(buffer), " \t")) == 0) {
+ /*
+ * Properly terminate the result, and reset the buffer write pointer for
+ * reading the next command. This is ugly, but not as ugly as trying to
+ * deal with all the early returns below.
+ */
+ vstring_truncate(state->buffer, VSTRING_LEN(state->buffer) - 2);
+ VSTRING_TERMINATE(state->buffer);
+ state->data_state = ST_ANY;
+ VSTRING_RESET(state->buffer);
+
+ /*
+ * Got a complete command line. Parse it.
+ */
+ if ((command = strtok(vstring_str(state->buffer), " \t")) == 0) {
smtp_printf(state->stream, "500 Error: unknown command");
return (0);
}
{
event_disable_readwrite(vstream_fileno(state->stream));
vstream_fclose(state->stream);
+ vstring_free(state->buffer);
myfree((char *) state);
}
non_blocking(fd, NON_BLOCKING);
state = (SINK_STATE *) mymalloc(sizeof(*state));
state->stream = vstream_fdopen(fd, O_RDWR);
+ state->buffer = vstring_alloc(1024);
state->read = command_read;
- state->data_state = 0;
+ state->data_state = ST_ANY;
smtp_timeout_setup(state->stream, var_tmout);
smtp_printf(state->stream, "220 %s ESMTP", var_myhostname);
event_enable_read(fd, read_event, (char *) state);
/*
* Initialize.
*/
- buffer = vstring_alloc(1024);
var_myhostname = "smtp-sink";
if (strncmp(argv[optind], "unix:", 5) == 0) {
sock = unix_listen(argv[optind] + 5, backlog, BLOCKING);
/* .IP "-w interval"
/* Wait a fixed time between messages.
/* Suspending one thread does not affect other delivery threads.
+/* BUGS
+/* No SMTP command pipelining support.
/* LICENSE
/* .ad
/* .fi
static void usage(char *myname)
{
- msg_fatal("usage: %s -s sess -l msglen -m msgs -c -C count -d -f from -o -t to -R delay -v -w delay host[:port]", myname);
+ msg_fatal("usage: %s -s sess -l msglen -m msgs -c -C count -d -f from -o -t to -r rcptcount -R delay -v -w delay host[:port]", myname);
}
/* main - parse JCL and start the machine */
select_bug: select_bug.c $(LIB)
$(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS)
-translit: $(LIB)
+translit: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-fsspace: $(LIB)
+fsspace: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-exec_command: $(LIB)
+exec_command: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-make_dirs: $(LIB)
+make_dirs: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-mac_parse: $(LIB)
+mac_parse: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-vstream_popen: $(LIB)
+vstream_popen: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-fifo_trigger: $(LIB)
+fifo_trigger: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-doze: $(LIB)
+doze: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-mac_expand: $(LIB)
+mac_expand: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-watchdog: $(LIB)
+watchdog: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-unescape: $(LIB)
+unescape: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-hex_quote: $(LIB)
+hex_quote: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-name_mask: $(LIB)
+name_mask: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-rand_sleep: $(LIB)
+rand_sleep: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-sane_time: $(LIB)
+sane_time: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-ctable: $(LIB)
+ctable: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-inet_addr_list: $(LIB)
+inet_addr_list: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
--- /dev/null
+a
+1
+b
+2
+c
+3
+d
+4
+e
+5
+f
+6
+f
+e
+d
+c
+b
+a
+1
+b
+c
+d
+e
+f
+6
+f
char *mystrdup(const char *str)
{
+ if (str == 0)
+ msg_panic("mystrdup: null pointer argument");
return (strcpy(mymalloc(strlen(str) + 1), str));
}
char *result;
char *cp;
+ if (str == 0)
+ msg_panic("mystrndup: null pointer argument");
if ((cp = memchr(str, 0, len)) != 0)
len = cp - str;
result = memcpy(mymalloc(len + 1), str, len);
char *mymemdup(const char *ptr, int len)
{
+ if (ptr == 0)
+ msg_panic("mymemdup: null pointer argument");
return (memcpy(mymalloc(len), ptr, len));
}
/* int vstream_setjmp(stream)
/* VSTREAM *stream;
/*
-/* void longjmp(stream, val)
+/* void vstream_longjmp(stream, val)
/* VSTREAM *stream;
/* int val;
/*