anntp

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

commit ccbb30dd2e6c844c9333a1bf3307e8f559717dee
parent 9d48529167584efb233efa581dcdab6b52f961a6
Author: Mario Rosell R. Martinez <mario@mariorosell.es>
Date:   Wed, 18 Mar 2026 19:56:44 +0100

Add anntp_write and TSLing...

Diffstat:
Manntp.h | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
1 file changed, 132 insertions(+), 49 deletions(-)

diff --git a/anntp.h b/anntp.h @@ -5,40 +5,36 @@ #ifndef ANNTP_H #define ANNTP_H -#include <arpa/inet.h> /* for inet_ntoa */ -#include <netdb.h> /* for gethostbyname */ -#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 */ -#include <unistd.h> /* for close */ - -/*** config ***/ +#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> #ifdef _WIN32 -# error This project uses the Anntp library, which is not compatible with Windows, sorry :( +# error This project uses the Anntp library, which is not compatible with Windows #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 @@ -54,86 +50,173 @@ enum anntp_state { typedef enum anntp_state AnntpState; -typedef struct anntp_connection AnntpConnection; - struct anntp_connection { - 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 from gethostbyname/gethostbyaddr */ - struct in_addr addr; /* resolved IP address */ - uchar_t read_buffer[ANNTP_BUFSIZE]; /* read buffer */ - size_t read_len; /* current length of data in buffer */ + int fd; + AnntpState state; + Bool use_tls; +#ifdef ANNTP_TLS + void* ssl; + void* ssl_ctx; +#endif + char* host; + struct in_addr addr; + uchar_t rbuf[ANNTP_BUFSIZE]; + size_t rbuf_elen; }; -ANNTP_API AnntpConnection* anntp_mkconn(const char* host, char* port, Bool tls); -ANNTP_API void anntp_freeconn(AnntpConnection* conn); -ANNTP_API int anntp_connect(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); +typedef struct anntp_connection AnntpConnection; + +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); #endif /* ANNTP_H */ #ifdef ANNTP_IMPLEMENTATION +#ifdef ANNTP_TLS +#include <openssl/ssl.h> +#include <openssl/err.h> +#endif + void anntp_init(void) { - /* - * for now, there is nothing to do here... - */ +#ifdef ANNTP_TLS + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); +#endif } AnntpConnection* -anntp_mkconn(const char* host, char* port, Bool tls) +anntp_mkconn(const char* host, const char* port, Bool tls) { - /* do some initting */ AnntpConnection* cv = (AnntpConnection*)ANNTP_MALLOC(sizeof(AnntpConnection)); if (!cv) - return NULL; /* check errno */ + return NULL; memset(cv, 0, sizeof(*cv)); cv->host = strdup(host); cv->use_tls = tls; cv->state = ANS_OFF; - /* first, we get a nice nonblocking two-way socket over TCP from the kernel... */ cv->fd = socket(AF_INET, SOCK_STREAM, 0); if (cv->fd < 0) { - perror("anntp - making connection socket"); + perror("anntp - socket"); goto cleanup; } - /* now resolve the host name... */ struct addrinfo hints, *res = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; - /* look mom! i made go in c! */ int err = getaddrinfo(host, port, &hints, &res); if (err != 0) { - fprintf(stderr, "anntp - cant resolve host `%s': %s", host, gai_strerror(err)); /* if you find whoever made this function of *HELL*, tell him how much i hate him */ + fprintf(stderr, "anntp - cant resolve host `%s': %s\n", host, gai_strerror(err)); close(cv->fd); goto cleanup; } - if (res) { - struct sockaddr_in* sa = (struct sockaddr_in*)res->ai_addr; - cv->addr = sa->sin_addr; + struct sockaddr_in* sa = (struct sockaddr_in*)res->ai_addr; + 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; } 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; + } + + SSL* ssl = SSL_new(ctx); + if (!ssl) { + fprintf(stderr, "anntp - SSL_new failed\n"); + SSL_CTX_free(ctx); + goto cleanup; + } + + 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; + } + + cv->ssl = ssl; + cv->ssl_ctx = ctx; + } +#endif + + cv->state = ANS_CONNECTING; return cv; cleanup: - ANNTP_FREE(cv->host); +#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); ANNTP_FREE(cv); return NULL; } +void +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); +#endif + + if (cv->fd >= 0) close(cv->fd); + if (cv->host) ANNTP_FREE(cv->host); + ANNTP_FREE(cv); +} + +ssize_t +anntp_write(AnntpConnection* conn, const uchar_t* bytes, size_t len) +{ + if (!conn || !bytes) + return -1; /* idiot-proofing */ + + size_t total = 0; + + while (total < len) { + ssize_t n = 0; + +#ifdef ANNTP_TLS + if (conn->use_tls) + n = SSL_write((SSL*)conn->ssl, bytes + total, (int)(len - total)); + else +#endif + n = write(conn->fd, bytes + total, len - total); + + if (n <= 0) { + /* an error happened */ + return n; + } + + total += n; + } +} + #endif /* ANNTP_IMPLEMENTATION */