anntp

a nntp implementation in pure C99
Log | Files | Refs | README | LICENSE

commit 09bda953d3b4050864a963801e3d6c952fb9fafa
parent 6978db7265432123927e85930c131872f971319c
Author: Mario Rosell R. Martinez <mario@mariorosell.es>
Date:   Fri, 20 Mar 2026 12:47:16 +0100

Make error handling clearer.

Made a new struct, AnntpErrCode, and added some error codes to it rather than
-1.  ssize_t-returning functions return the error code in negative.

Diffstat:
Manntp.h | 122++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 90 insertions(+), 32 deletions(-)

diff --git a/anntp.h b/anntp.h @@ -48,6 +48,9 @@ #define Bool int #define true 1 #define false 0 +#define ANNTPE(e) (-(ssize_t)(e)) +#define ANNTP_ISERR(x) ((x) < 0) +#define ANNTP_CODE(x) ((AnntpErrCode)(-(x))) typedef unsigned char uchar_t; @@ -58,7 +61,18 @@ enum anntp_state { ANS_ERROR, }; +/* NOTE: often given as negative for functions returning ssize_t */ +enum anntp_errcode { + ANE_OK = 0, + ANE_PARAMS, + ANE_TLS, + ANE_IO, + ANE_PROTO, + ANE_AUTH, +}; + typedef enum anntp_state AnntpState; +typedef enum anntp_errcode AnntpErrCode; struct anntp_connection { int fd; /* socket file descriptor */ @@ -83,15 +97,16 @@ typedef int (*AnntpLineCb)(const char* line, void* extra); ANNTP_API void anntp_init(void); ANNTP_API AnntpConnection* anntp_mkconn(const char* host, const char* port, Bool tls); -ANNTP_API void anntp_freeconn(AnntpConnection* conn); -ANNTP_API ssize_t anntp_read(AnntpConnection* conn, uchar_t* buf, size_t bufsize); -ANNTP_API ssize_t anntp_write(AnntpConnection* conn, const uchar_t* buf, size_t bufsize); -ANNTP_API ssize_t anntp_write_all(AnntpConnection* conn, const uchar_t* buf, size_t bufsize); -ANNTP_API ssize_t anntp_writeline(AnntpConnection* conn, const char* buf); -ANNTP_API ssize_t anntp_readline(AnntpConnection* conn, char* buf, size_t maxlen); -ANNTP_API ssize_t anntp_readdot(AnntpConnection* conn, char* buf, size_t maxlen); -ANNTP_API int anntp_readdot_cb(AnntpConnection* conn, AnntpLineCb cb, void* extra); -ANNTP_API int anntp_auth(AnntpConnection* conn, const char* login, const char* password); +ANNTP_API void anntp_freeconn(AnntpConnection* conn); +ANNTP_API ssize_t anntp_read(AnntpConnection* conn, uchar_t* buf, size_t bufsize); +ANNTP_API ssize_t anntp_write(AnntpConnection* conn, const uchar_t* buf, size_t bufsize); +ANNTP_API ssize_t anntp_write_all(AnntpConnection* conn, const uchar_t* buf, size_t bufsize); +ANNTP_API ssize_t anntp_writeline(AnntpConnection* conn, const char* buf); +ANNTP_API ssize_t anntp_readline(AnntpConnection* conn, char* buf, size_t maxlen); +ANNTP_API ssize_t anntp_readdot(AnntpConnection* conn, char* buf, size_t maxlen); +ANNTP_API int anntp_readdot_cb(AnntpConnection* conn, AnntpLineCb cb, void* extra); +ANNTP_API int anntp_auth(AnntpConnection* conn, const char* login, const char* password); +ANNTP_API char* anntp_strerr(AnntpErrCode err); #endif /* ANNTP_H */ @@ -209,25 +224,33 @@ anntp_freeconn(AnntpConnection* cv) ssize_t anntp_read(AnntpConnection* cv, uchar_t* buf, size_t len) { - if (!cv || !buf) return -1; + if (!cv || !buf) ANNTPE(ANE_PARAMS); #ifdef ANNTP_TLS - if (cv->use_tls && cv->ssl) return SSL_read(cv->ssl, buf, (int)len); + if (cv->use_tls && cv->ssl) { + int n = SSL_read(cv->ssl, buf, (int)len); + return (n <= 0) ? ANNTPE(ANE_TLS) : n; + } #endif - return read(cv->fd, buf, len); + ssize_t n = read(cv->fd, buf, len); + return (n < 0) ? ANNTPE(ANE_IO) : n; } ssize_t anntp_write(AnntpConnection* cv, const uchar_t* buf, size_t len) { - if (!cv || !buf) return -1; + if (!cv || !buf) return ANNTPE(ANE_PARAMS); #ifdef ANNTP_TLS - if (cv->use_tls && cv->ssl) return SSL_write(cv->ssl, buf, (int)len); + if (cv->use_tls && cv->ssl) { + int n = SSL_write(cv->ssl, buf, (int)len); + return (n <= 0) ? ANNTPE(ANE_TLS) : n; + } #endif - return write(cv->fd, buf, len); + ssize_t n = write(cv->fd, buf, len); + return (n < 0) ? ANNTPE(ANE_IO) : write(cv->fd, buf, len); } ssize_t @@ -245,7 +268,7 @@ anntp_write_all(AnntpConnection* cv, const uchar_t* buf, size_t len) ssize_t anntp_readline(AnntpConnection* cv, char* buf, size_t maxlen) { - if (!cv || !buf || maxlen == 0) return -1; + if (!cv || !buf || maxlen == 0) return ANNTPE(ANE_PARAMS); size_t pos = 0; @@ -325,7 +348,7 @@ anntp_readdot(AnntpConnection* cv, char* buf, size_t maxlen) int anntp_readdot_cb(AnntpConnection* cv, AnntpLineCb cb, void* extra) { - if (!cv || !cb) return -1; + if (!cv || !cb) return ANE_PARAMS; char line[ANNTP_BUFSIZE]; ssize_t n; @@ -333,7 +356,7 @@ anntp_readdot_cb(AnntpConnection* cv, AnntpLineCb cb, void* extra) for (;;) { n = anntp_readline(cv, line, sizeof(line)); if (n <= 0) - return -1; + return ANE_IO; /* logic here is a bit like on anntp_readdot */ if (strcmp(line, ".\r\n") == 0) @@ -344,10 +367,10 @@ anntp_readdot_cb(AnntpConnection* cv, AnntpLineCb cb, void* extra) out++; if (cb(out, extra) != 0) - return 1; /* user aborted */ + return ANE_OK; /* user aborted */ } - return 0; + return ANE_OK; } /* NOTE: I have a gut feeling this auth mechanism is probably a bit insecure... */ @@ -363,7 +386,7 @@ anntp_auth(AnntpConnection* cv, const char* user, const char* pass) */ if (!cv || !user || !pass) - return -1; + return ANE_PARAMS; char line[ANNTP_BUFSIZE]; char cmd[ANNTP_BUFSIZE]; @@ -374,21 +397,21 @@ anntp_auth(AnntpConnection* cv, const char* user, const char* pass) n = anntp_writeline(cv, cmd); if (n <= 0) - return -1; + return ANE_IO; /* read response */ n = anntp_readline(cv, line, sizeof(line)); if (n <= 0) - return -1; + return ANE_IO; /* already authenticated */ if (strncmp(line, "281", 3) == 0) { - return 0; + return ANE_OK; /* it isn't an error to be already authenticated */ } /* must be 381 to continue */ if (strncmp(line, "381", 3) != 0) { - return -1; + return ANE_PROTO; /* that isn't a valid response, probably */ } snprintf(cmd, sizeof(cmd), "AUTHINFO PASS %s", pass); @@ -399,19 +422,19 @@ anntp_auth(AnntpConnection* cv, const char* user, const char* pass) memset(cmd, 0, sizeof(cmd)); if (n <= 0) - return -1; + return ANE_IO; n = anntp_readline(cv, line, sizeof(line)); if (n <= 0) - return -1; + return ANE_IO; /* success */ if (strncmp(line, "281", 3) == 0) { - return 0; + return ANE_OK; } /* failure */ - return -1; + return ANE_AUTH; } /* fill read buffer with data */ @@ -430,11 +453,46 @@ _afillbuf(AnntpConnection* c) if (n > 0) { c->read_len = (size_t)n; c->read_pos = 0; - } else { - c->read_len = 0; + return n; } - return n; + c->read_len = 0; + + if (n == 0) + return 0; /* EOF */ + +#ifdef ANNTP_TLS + if (c->use_tls && c->ssl) + return ANNTPE(ANE_TLS); +#endif + + return ANNTPE(ANE_IO); +} + +char* +anntp_strerror(AnntpErrCode c) +{ + /* ssize_t-returning functions will report errors in negative */ + if (c < 0) + c = (AnntpErrCode)(-c); /* "i never said const!" */ + + switch (c) { + case ANE_OK: + return "<ok>"; + case ANE_PARAMS: + return "invalid parameters"; + case ANE_TLS: + return "security (cryptography/TLS) error"; + case ANE_IO: + return "I/O error"; + case ANE_PROTO: + return "protocol error, unexpected server response"; + case ANE_AUTH: + return "authentication failed"; + + default: + return "unknown error"; + } } #endif /* ANNTP_IMPLEMENTATION */