Error handling

D

dgiagio

Hi,

I'm creating a SMTP application and I would like to hear opinions about
error handling. Currently there are two functions that communicate with
the remote peer:

ssize_t smtp_puts(smtp_stream_t *stream, const char *fmt, ...);
ssize_t smtp_gets(smtp_stream_t *stream, char *buf, size_t len);

Both of them return the number of written bytes to the stream, or -1 if
an error ocurred. The error is set on errno, since they use write() and
read().

My approach is like the following:

if (smtp_puts(s, "HELO hostname") == -1) {
syslog(LOG_ERR, "smtp_puts: sending HELO: %m");
goto cleanup;
}

if (smtp_gets(s, buf, sizeof(buf) == -1) {
syslog(LOG_ERR," smtp_gets: receiving HELO: %m");
goto cleanup;
}

/* Process the HELO response */
if (smtp_check_helo(buf) == -1) {
syslog(LOG_ERR, "smtp_check_helo: bad HELO");
goto cleanup;
}

if (smtp_puts(s, "MAIL FROM:<postmaster>") == -1) {
syslog(LOG_ERR, "smtp_puts: sending MAIL FROM: %m");
goto cleanup;
}

if (smtp_gets(s, buf, sizeof(buf) == -1) {
syslog(LOG_ERR," smtp_gets: receiving MAIL FROM: %m");
goto cleanup;
}

/* Process the MAIL FROM response */
if (smtp_check_mailfrom(buf) == -1) {
syslog(LOG_ERR, "smtp_check_mailfrom: bad MAIL FROM");
goto cleanup;
}

/* And this keep going... RCPT TO, DATA, RSET, QUIT, etc... */

cleanup:
smtp_stream_close(s);
return 0;


I've been reading Postfix sources and it seems they use setjmp/longjmp
for exceptions on . I don't think that's a good approach, so I'm asking
it here. Qmail's approach is to _exit() on most errors, which is ugly.

What would be a 'good' error handling mechanism for this kind of
situation?
Thanks
 
F

Flash Gordon

Hi,

I'm creating a SMTP application and I would like to hear opinions about
error handling. Currently there are two functions that communicate with
the remote peer:

ssize_t smtp_puts(smtp_stream_t *stream, const char *fmt, ...);
ssize_t smtp_gets(smtp_stream_t *stream, char *buf, size_t len);

Both of them return the number of written bytes to the stream, or -1 if
an error ocurred. The error is set on errno, since they use write() and
read().

For your information (and this is not a complaint) if you have problems
with read/write then please ask them else where, maybe
comp.unix.programmer, since we only deal with standard C not Posix or
Windows specifics.

Your question is not to do with read/write and you provide enough
information about the function your don't provide, so it is entirely
topical.
My approach is like the following:

if (smtp_puts(s, "HELO hostname") == -1) {
syslog(LOG_ERR, "smtp_puts: sending HELO: %m");
goto cleanup;
}

if (smtp_gets(s, buf, sizeof(buf) == -1) {
syslog(LOG_ERR," smtp_gets: receiving HELO: %m");
goto cleanup;
}

/* Process the HELO response */
if (smtp_check_helo(buf) == -1) {
syslog(LOG_ERR, "smtp_check_helo: bad HELO");
goto cleanup;
}

if (smtp_puts(s, "MAIL FROM:<postmaster>") == -1) {
syslog(LOG_ERR, "smtp_puts: sending MAIL FROM: %m");
goto cleanup;
}

if (smtp_gets(s, buf, sizeof(buf) == -1) {
syslog(LOG_ERR," smtp_gets: receiving MAIL FROM: %m");
goto cleanup;
}

/* Process the MAIL FROM response */
if (smtp_check_mailfrom(buf) == -1) {
syslog(LOG_ERR, "smtp_check_mailfrom: bad MAIL FROM");
goto cleanup;
}

/* And this keep going... RCPT TO, DATA, RSET, QUIT, etc... */

cleanup:
smtp_stream_close(s);
return 0;

<OT>
My reading of the RFC for SMTP suggested that you should send an "RSET"
if you are part way through sending the email and decide to give up, and
that you should send a "QUIT" to log off the mail server. Obviously, the
mail server has to handle it properly if you just drop the connection,
so it's no big deal.

Don't bother correcting me if I'm wrong because it's off topic here and
I would not have mentioned it at all if I was not responding to your
main point.
I've been reading Postfix sources and it seems they use setjmp/longjmp
for exceptions on . I don't think that's a good approach, so I'm asking
it here. Qmail's approach is to _exit() on most errors, which is ugly.

As far as I am aware C provides exit(), abort() and, with the new C99
standard, _Exit() but does not provide an _exit() function.
What would be a 'good' error handling mechanism for this kind of
situation?

Of the three solutions you have presented I prefer yours. However, I'll
present another solution, one I used when writing a simple SMTP client
some years ago was something like:

static int dirtysendmail(parameters...)
{
/* do stuff */
if (whatever)
return MY_ERR1;
/* do more stuff */
if (whateverelse)
return MY_ERR2;
/* and so on */
return 0; /* success */
}

int sendmail(parameters...)
{
int ret=dirtysendmail(parameters...);
switch (ret) {
case default: /* try to send a RSET */
case 0: /* try to send a QUIT */
case ERR_MEANING_NOT_EVEN_GOT_HELO_OUT: /* try to close socket */
}
return ret;
}

Note the lack of breaks in the switch so that it falls through and the
amount of tidying up done depends on how far it got.

In my case exiting the application was not appropriate since the smtp
client is embedded within a larger application, and people don't want to
be kicked out just because it can't send email notifications! With qmail
I would guess you are looking at a program that sends (or fails to send)
one email then exits, so exiting as soon as it has failed is not
necessarily unreasonable.
 
K

Keith Thompson

Flash Gordon said:
int sendmail(parameters...)
{
int ret=dirtysendmail(parameters...);
switch (ret) {
case default: /* try to send a RSET */
case 0: /* try to send a QUIT */
case ERR_MEANING_NOT_EVEN_GOT_HELO_OUT: /* try to close socket */
}
return ret;
}

Note the lack of breaks in the switch so that it falls through and the
amount of tidying up done depends on how far it got.

And since you're showing us pseudo-code, the "case default:" is an
acceptable way of writing the syntactically correct "default:".
 
F

Flash Gordon

And since you're showing us pseudo-code, the "case default:" is an
acceptable way of writing the syntactically correct "default:".

Yes, that is!

<fx:wanders off muttering/>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top