anntp

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

commit f7f20a74dd85bafbf0350d9be0556abbd13ec81b
parent ccbb30dd2e6c844c9333a1bf3307e8f559717dee
Author: Mario Rosell R. Martinez <mario@mariorosell.es>
Date:   Wed, 18 Mar 2026 20:33:35 +0100

Add write.

Diffstat:
M.gitignore | 3+--
Manntp.h | 222+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
2 files changed, 139 insertions(+), 86 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,5 +1,4 @@ -test.c -*.o +*.c a.out thumbs.db .DS_Store diff --git a/anntp.h b/anntp.h @@ -5,36 +5,46 @@ #ifndef ANNTP_H #define ANNTP_H -#include <arpa/inet.h> -#include <netdb.h> -#include <netinet/in.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> -#include <unistd.h> +#include <arpa/inet.h> /* for inet_ntoa, inet_ntop */ +#include <netdb.h> /* for gethostbyname, struct hostent */ +#include <netinet/in.h> /* for struct in_addr */ +#include <stddef.h> /* for size_t */ +#include <stdio.h> /* for fprintf, perror, stderr */ +#include <stdlib.h> /* for malloc, free */ +#include <string.h> /* for memset, memcpy, strdup */ +#include <sys/socket.h> /* for socket, connect */ +#include <unistd.h> /* for close */ +#include <fcntl.h> /* for fcntl */ + +#ifdef ANNTP_TLS +#include <openssl/ssl.h> +#include <openssl/err.h> +#endif + +/*** config ***/ #ifdef _WIN32 -# error This project uses the Anntp library, which is not compatible with Windows +# error This project uses the Anntp library, which is not compatible with Windows, sorry :( #endif #ifndef ANNTP_MALLOC -# define ANNTP_MALLOC malloc +# define ANNTP_MALLOC malloc #endif #ifndef ANNTP_FREE -# define ANNTP_FREE free +# define ANNTP_FREE free #endif #ifndef ANNTP_BUFSIZE -# define ANNTP_BUFSIZE 0x1000 +# define ANNTP_BUFSIZE 0x1000 #endif #ifndef ANNTP_API -# define ANNTP_API extern +# define ANNTP_API extern #endif +/*** defs ***/ + #define Bool int #define true 1 #define false 0 @@ -51,73 +61,79 @@ enum anntp_state { typedef enum anntp_state AnntpState; struct anntp_connection { - int fd; - AnntpState state; - Bool use_tls; + int fd; /* socket file descriptor */ + AnntpState state; /* current connection state */ + Bool use_tls; /* whether to use TLS */ + char* host; /* hostname string */ + struct hostent* host_he; /* resolved host entry */ + struct in_addr addr; /* resolved IP address */ + uchar_t read_buffer[ANNTP_BUFSIZE]; /* read buffer */ + size_t read_len; /* length of data in buffer */ #ifdef ANNTP_TLS - void* ssl; - void* ssl_ctx; + SSL* ssl; /* TLS session */ #endif - char* host; - struct in_addr addr; - uchar_t rbuf[ANNTP_BUFSIZE]; - size_t rbuf_elen; }; typedef struct anntp_connection AnntpConnection; -ANNTP_API void anntp_init(void); +/*** function declarations ***/ + +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 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_write_line(AnntpConnection* conn, const uchar_t* buf, size_t bufsize); +ANNTP_API ssize_t anntp_readline(AnntpConnection* conn, char* buf, size_t maxlen); #endif /* ANNTP_H */ #ifdef ANNTP_IMPLEMENTATION -#ifdef ANNTP_TLS -#include <openssl/ssl.h> -#include <openssl/err.h> -#endif - void anntp_init(void) { #ifdef ANNTP_TLS - SSL_library_init(); SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); + SSL_library_init(); + OpenSSL_add_ssl_algorithms(); #endif } +/* create connection */ AnntpConnection* anntp_mkconn(const char* host, const char* port, Bool tls) { AnntpConnection* cv = (AnntpConnection*)ANNTP_MALLOC(sizeof(AnntpConnection)); - if (!cv) - return NULL; + if (!cv) return NULL; memset(cv, 0, sizeof(*cv)); cv->host = strdup(host); cv->use_tls = tls; cv->state = ANS_OFF; + /* create socket */ cv->fd = socket(AF_INET, SOCK_STREAM, 0); if (cv->fd < 0) { - perror("anntp - socket"); + perror("anntp - making socket"); goto cleanup; } + /* make socket blocking for greeting */ + int flags = fcntl(cv->fd, F_GETFL, 0); + flags &= ~O_NONBLOCK; + fcntl(cv->fd, F_SETFL, flags); + + /* resolve host */ struct addrinfo hints, *res = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; int err = getaddrinfo(host, port, &hints, &res); - if (err != 0) { + if (err != 0 || !res) { fprintf(stderr, "anntp - cant resolve host `%s': %s\n", host, gai_strerror(err)); - close(cv->fd); goto cleanup; } @@ -125,52 +141,41 @@ anntp_mkconn(const char* host, const char* port, Bool tls) cv->addr = sa->sin_addr; if (connect(cv->fd, (struct sockaddr*)sa, sizeof(*sa)) < 0) { - perror("anntp - connect"); - close(cv->fd); - freeaddrinfo(res); - goto cleanup; + perror("anntp - connect failed"); + goto cleanup_addr; } - freeaddrinfo(res); + cv->state = ANS_CONNECTING; #ifdef ANNTP_TLS if (tls) { SSL_CTX* ctx = SSL_CTX_new(TLS_client_method()); - if (!ctx) { - fprintf(stderr, "anntp - SSL_CTX_new failed\n"); - goto cleanup; - } + if (!ctx) goto cleanup_addr; - SSL* ssl = SSL_new(ctx); - if (!ssl) { - fprintf(stderr, "anntp - SSL_new failed\n"); - SSL_CTX_free(ctx); - goto cleanup; - } + cv->ssl = SSL_new(ctx); + if (!cv->ssl) goto cleanup_addr; - SSL_set_fd(ssl, cv->fd); - if (SSL_connect(ssl) <= 0) { - ERR_print_errors_fp(stderr); - SSL_free(ssl); - SSL_CTX_free(ctx); - goto cleanup; + SSL_set_fd(cv->ssl, cv->fd); + if (SSL_connect(cv->ssl) <= 0) { + SSL_free(cv->ssl); + cv->ssl = NULL; + goto cleanup_addr; } - cv->ssl = ssl; - cv->ssl_ctx = ctx; + SSL_CTX_free(ctx); } #endif - cv->state = ANS_CONNECTING; + cv->state = ANS_READY; + + freeaddrinfo(res); return cv; +cleanup_addr: + freeaddrinfo(res); cleanup: -#ifdef ANNTP_TLS - if (cv->ssl) SSL_free((SSL*)cv->ssl); - if (cv->ssl_ctx) SSL_CTX_free((SSL_CTX*)cv->ssl_ctx); -#endif - if (cv->fd >= 0) close(cv->fd); if (cv->host) ANNTP_FREE(cv->host); + if (cv->fd >= 0) close(cv->fd); ANNTP_FREE(cv); return NULL; } @@ -181,9 +186,10 @@ anntp_freeconn(AnntpConnection* cv) if (!cv) return; #ifdef ANNTP_TLS - if (cv->ssl) SSL_shutdown((SSL*)cv->ssl); - if (cv->ssl) SSL_free((SSL*)cv->ssl); - if (cv->ssl_ctx) SSL_CTX_free((SSL_CTX*)cv->ssl_ctx); + if (cv->ssl) { + SSL_shutdown(cv->ssl); + SSL_free(cv->ssl); + } #endif if (cv->fd >= 0) close(cv->fd); @@ -192,30 +198,78 @@ anntp_freeconn(AnntpConnection* cv) } ssize_t -anntp_write(AnntpConnection* conn, const uchar_t* bytes, size_t len) +anntp_read(AnntpConnection* cv, uchar_t* buf, size_t len) { - if (!conn || !bytes) - return -1; /* idiot-proofing */ + if (!cv || !buf) return -1; - size_t total = 0; +#ifdef ANNTP_TLS + if (cv->use_tls && cv->ssl) return SSL_read(cv->ssl, buf, (int)len); +#endif + + return read(cv->fd, buf, len); +} - while (total < len) { - ssize_t n = 0; +ssize_t +anntp_write(AnntpConnection* cv, const uchar_t* buf, size_t len) +{ + if (!cv || !buf) return -1; #ifdef ANNTP_TLS - if (conn->use_tls) - n = SSL_write((SSL*)conn->ssl, bytes + total, (int)(len - total)); - else + if (cv->use_tls && cv->ssl) return SSL_write(cv->ssl, buf, (int)len); #endif - n = write(conn->fd, bytes + total, len - total); + return write(cv->fd, buf, len); +} + +ssize_t +anntp_write_all(AnntpConnection* cv, const uchar_t* buf, size_t len) +{ + size_t sent = 0; + while (sent < len) { + ssize_t n = anntp_write(cv, buf + sent, len - sent); + if (n <= 0) return n; + sent += (size_t)n; + } + return (ssize_t)sent; +} + +ssize_t +anntp_readline(AnntpConnection* cv, char* buf, size_t maxlen) +{ + if (!cv || !buf || maxlen == 0) return -1; + + size_t pos = 0; + while (pos < maxlen - 1) { + uchar_t c; + ssize_t n = anntp_read(cv, &c, 1); if (n <= 0) { - /* an error happened */ - return n; + if (pos == 0) return n; + break; } - total += n; + buf[pos++] = c; + if (c == '\n') break; } + + buf[pos] = '\0'; + return (ssize_t)pos; +} + +ssize_t +anntp_writeline(AnntpConnection* cv, const char* line) +{ + if (!cv || !line) return -1; + + size_t len = strlen(line); + ssize_t n; + + /* write the line itself */ + n = anntp_write_all(cv, (const uchar_t*)line, len); + if (n <= 0) return n; + + /* write CRLF */ + n = anntp_write_all(cv, (const uchar_t*)"\r\n", 2); + return n; } #endif /* ANNTP_IMPLEMENTATION */