BearSSL
public
Read
Owner: themaster
Branch: main
Commits: 2
Updated: 2026-04-19 00:20
Git CLI clone URL
git clone https://www.xt-emporium.com/git/bearssl.git
Fullscreen desktop URL
Code
Commits
History
Branches
Bug Reports
Discussions
Compare
Settings
bearssl
/
tlsget.c
File editor
#include <arpa/inet.h> #include <errno.h> #include <netinet/in.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <time.h> #include <unistd.h> #include "bearssl.h" static int net_connect(const char *host, unsigned short port) { int fd, e; struct linger l; struct sockaddr_in sa; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { return -1; } memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = PORT_ANY; sa.sin_addr.s_addr = INADDR_ANY; if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { goto error; } memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = in_gethostbyname(host); if (!sa.sin_addr.s_addr) { errno = ENOENT; goto error; } l.l_onoff = 1; l.l_linger = 0; setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { goto error; } return fd; error: e = errno; close(fd); errno = e; return -1; } static int net_close(int fd, int errflag) { if (!errflag) { struct linger l; l.l_onoff = 0; l.l_linger = 0; setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); } return close(fd); } static int sock_read(void *ctx, unsigned char *buf, size_t len) { for (;;) { int rlen; rlen = read(*(int *)ctx, buf, len); if (rlen <= 0) { if (rlen < 0 && errno == EINTR) { continue; } return -1; } return rlen; } } static int sock_write(void *ctx, const unsigned char *buf, size_t len) { for (;;) { int wlen; wlen = write(*(int *)ctx, buf, len); if (wlen <= 0) { if (wlen < 0 && errno == EINTR) { continue; } return -1; } return wlen; } } static void seed_rng(br_ssl_client_context *sc, const char *host, unsigned short port) { unsigned char seed[64]; unsigned long mix; size_t i; mix = (unsigned long)time(NULL); mix ^= ((unsigned long)getpid() << 8); mix ^= ((unsigned long)port << 16); while (*host) { mix = (mix << 5) - mix + (unsigned char)*host++; } for (i = 0; i < sizeof(seed); i++) { mix = mix * 1103515245UL + 12345UL + i; seed[i] = (unsigned char)(mix >> 16); } br_ssl_engine_inject_entropy(&sc->eng, seed, sizeof(seed)); } static unsigned long gregorian_days(int year, int month, int day) { static const unsigned short month_to_days[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; unsigned long days; int leap; days = (unsigned long)year * 365UL; days += (unsigned long)(year / 4); days -= (unsigned long)(year / 100); days += (unsigned long)(year / 400); days += month_to_days[month - 1]; leap = ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0); if (leap && month > 2) days++; days += (unsigned long)(day - 1); return days; } static void set_validation_time(br_x509_minimal_context *xc) { time_t now; struct tm *tm; unsigned long days; unsigned long seconds; now = time(NULL); if (now <= 0) now = (time_t)1772409600L; tm = gmtime(&now); if (tm == NULL) return; days = gregorian_days(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); seconds = (unsigned long)tm->tm_hour * 3600UL; seconds += (unsigned long)tm->tm_min * 60UL; seconds += (unsigned long)tm->tm_sec; br_x509_minimal_set_time(xc, days, seconds); } static const unsigned char TA_DN[] = { 0x30, 0x1C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x04, 0x52, 0x6F, 0x6F, 0x74 }; static const unsigned char TA_EC_Q[] = { 0x04, 0x71, 0x74, 0xBA, 0xAB, 0xB9, 0x30, 0x2E, 0x81, 0xD5, 0xE5, 0x57, 0xF9, 0xF3, 0x20, 0x68, 0x0C, 0x9C, 0xF9, 0x64, 0xDB, 0xB4, 0x20, 0x0D, 0x6D, 0xEA, 0x40, 0xD0, 0x4A, 0x6E, 0x42, 0xFD, 0xB6, 0x9A, 0x68, 0x25, 0x44, 0xF6, 0xDF, 0x7B, 0xC4, 0xFC, 0xDE, 0xDD, 0x7B, 0xBB, 0xC5, 0xDB, 0x7C, 0x76, 0x3F, 0x41, 0x66, 0x40, 0x6E, 0xDB, 0xA7, 0x87, 0xC2, 0xE5, 0xD8, 0xC5, 0xF3, 0x7F, 0x8D }; static const br_x509_trust_anchor TAs[] = { { { (unsigned char *)TA_DN, sizeof(TA_DN) }, BR_X509_TA_CA, { BR_KEYTYPE_EC, { .ec = { BR_EC_secp256r1, (unsigned char *)TA_EC_Q, sizeof(TA_EC_Q) } } } } }; struct tls_state { char req[256]; char header[1024]; unsigned char buf[512]; size_t header_len; long content_length; long body_received; int header_done; br_ssl_client_context sc; br_sslio_context ioc; br_x509_minimal_context xc; }; static int header_end(const char *buf, size_t len) { size_t i; if (len < 4) return -1; for (i = 3; i < len; i++) { if (buf[i - 3] == '\r' && buf[i - 2] == '\n' && buf[i - 1] == '\r' && buf[i] == '\n') { return (int)(i + 1); } } return -1; } static void parse_headers(struct tls_state *state) { char *line; state->header[state->header_len] = '\0'; line = strstr(state->header, "\r\n"); if (line == NULL) return; line += 2; while (*line != '\0' && !(line[0] == '\r' && line[1] == '\n')) { char *next; next = strstr(line, "\r\n"); if (next == NULL) break; if (strncasecmp(line, "Content-Length:", 15) == 0) { char *value; value = line + 15; while (*value == ' ' || *value == '\t') value++; state->content_length = atol(value); } line = next + 2; } } static int consume_http(struct tls_state *state, const unsigned char *buf, size_t len) { size_t copy_len; int body_off; if (!state->header_done) { copy_len = len; if (state->header_len + copy_len >= sizeof(state->header)) { copy_len = sizeof(state->header) - state->header_len - 1; } if (copy_len != 0) { memcpy(state->header + state->header_len, buf, copy_len); state->header_len += copy_len; } body_off = header_end(state->header, state->header_len); if (body_off >= 0) { state->header_done = 1; parse_headers(state); if ((size_t)body_off < state->header_len) { state->body_received += (long)(state->header_len - (size_t)body_off); } } } else { state->body_received += (long)len; } if (state->content_length >= 0 && state->header_done && state->body_received >= state->content_length) { return 1; } return 0; } int main(int argc, char *argv[]) { const char *host; const char *servername; const char *path; unsigned short port; int fd; int err; int complete; unsigned char *iobuf; struct tls_state *state; if (argc < 3 || argc > 6) { fprintf(stderr, "usage: %s [-n servername] host port [path]\n", argv[0]); return 1; } servername = NULL; if (argc > 2 && strcmp(argv[1], "-n") == 0) { servername = argv[2]; argv += 2; argc -= 2; } host = argv[1]; port = (unsigned short)atoi(argv[2]); path = argc > 3 ? argv[3] : "/"; if (servername == NULL) { servername = host; } if (!port) { fprintf(stderr, "%s: invalid port\n", argv[2]); return 1; } signal(SIGPIPE, SIG_IGN); fd = net_connect(host, port); if (fd < 0) { perror("connect"); return 1; } state = malloc(sizeof(*state)); if (state == NULL) { perror("malloc"); net_close(fd, 1); return 1; } memset(state, 0, sizeof(*state)); state->content_length = -1; iobuf = malloc(BR_SSL_BUFSIZE_MONO); if (iobuf == NULL) { perror("malloc"); free(state); net_close(fd, 1); return 1; } br_ssl_client_init_full(&state->sc, &state->xc, TAs, 1); seed_rng(&state->sc, servername, port); set_validation_time(&state->xc); br_ssl_engine_set_buffer(&state->sc.eng, iobuf, BR_SSL_BUFSIZE_MONO, 0); if (!br_ssl_client_reset(&state->sc, servername, 0)) { fprintf(stderr, "tlsget: ssl reset failed\n"); free(state); free(iobuf); net_close(fd, 1); return 1; } br_sslio_init(&state->ioc, &state->sc.eng, sock_read, &fd, sock_write, &fd); snprintf(state->req, sizeof(state->req), "GET %s HTTP/1.0\r\nHost: %s\r\nConnection: close\r\n\r\n", path, host); if (br_sslio_write_all(&state->ioc, state->req, strlen(state->req)) < 0 || br_sslio_flush(&state->ioc) < 0) { fprintf(stderr, "tlsget: write failed (%d)\n", br_ssl_engine_last_error(&state->sc.eng)); free(state); free(iobuf); net_close(fd, 1); return 1; } for (;;) { int rlen; rlen = br_sslio_read(&state->ioc, state->buf, sizeof(state->buf)); if (rlen < 0) { break; } if (write(STDOUT_FILENO, state->buf, rlen) != rlen) { perror("write"); free(state); free(iobuf); net_close(fd, 1); return 1; } if (consume_http(state, state->buf, rlen)) { break; } } if (state->header_done && state->content_length >= 0 && state->body_received >= state->content_length) { br_sslio_close(&state->ioc); } complete = state->header_done && state->content_length >= 0 && state->body_received >= state->content_length; net_close(fd, complete ? 0 : 1); if (br_ssl_engine_current_state(&state->sc.eng) != BR_SSL_CLOSED && !complete) { fprintf(stderr, "tlsget: socket closed without close_notify\n"); free(state); free(iobuf); return 1; } err = br_ssl_engine_last_error(&state->sc.eng); free(state); free(iobuf); if (err != 0 && !(err == BR_ERR_IO && complete)) { fprintf(stderr, "tlsget: ssl error %d\n", err); return 1; } return 0; }
Commit message
This repository is read-only for this account.
Repository snapshot
Current branch
main
Visibility
public
Your access
Read
Remote
None
File activity
View file history