#define DEBUG 1 #include #include #include #include #include #include #include #include #include #include #include "tlse.c" // get proper sockaddr, ipv4 or ipv6 void *get_in_addr(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { return &(((struct sockaddr_in*)sa)->sin_addr); } return &(((struct sockaddr_in6*)sa)->sin6_addr); } // returns an addrinfo linked list. needs to be freed // with freeaddrinfo later! struct addrinfo *get_addr_info(const char *host, const char *port) { struct addrinfo hints, *res; int status; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ((status = getaddrinfo(host, port, &hints, &res)) != 0) { fprintf(stderr, "[net] getaddrinfo failed: %s\n", gai_strerror(status)); return NULL; } return res; } int getsock(struct addrinfo *info) { struct addrinfo *p; int sockfd; char ipstr[INET6_ADDRSTRLEN]; for (p = info; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { continue; } if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); continue; } break; } if (p == NULL) { fprintf(stderr, "[net] client failed to connect.\n"); exit(8); } inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), ipstr, sizeof ipstr); printf("[net\tinfo] connecting to %s...\n", ipstr); printf("[net\tinfo] connected.\n"); return sockfd; } int send_pending(int sock, struct TLSContext* ctx) { unsigned int out_buf_len = 0; unsigned int out_buf_idx = 0; const unsigned char* out_buf; unsigned int send_res, res; out_buf = tls_get_write_buffer(ctx, &out_buf_len); while ((out_buf) && (out_buf_len > 0)) { res = send(sock, &out_buf[out_buf_idx], out_buf_len, 0); if (res <= 0) { send_res = res; break; } out_buf_len -= res; out_buf_idx += res; } tls_buffer_clear(ctx); return send_res; } int do_validate_cert(struct TLSContext* c, struct TLSCertificate** chain, int len) { return no_error; } int main() { struct addrinfo* info = get_addr_info("gemi.dev", "1965"); int fd = getsock(info); fcntl(fd, F_SETFL, O_NONBLOCK); freeaddrinfo(info); struct TLSContext *context; context = tls_create_context(0, TLS_V12); tls_sni_set(context, "gemi.dev"); tls_client_connect(context); send_pending(fd, context); fd_set fds; unsigned char client_message[8192]; unsigned char read_buffer[0xFFFF]; bool sent = false; while (true) { FD_ZERO(&fds); FD_SET(fd, &fds); select(fd + 1, &fds, NULL, NULL, NULL); if (FD_ISSET(fd, &fds)) { size_t nread = recv(fd, client_message, sizeof(client_message), 0); if (nread > 0) { int i = tls_consume_stream(context, client_message, nread, do_validate_cert); if (i < 0) { printf("*** error in tls_consume_stream: %d\n", i); break; } send_pending(fd, context); if (!tls_established(context)) continue; if (!sent) { char text[] = "gemini://gemi.dev/\r\n"; tls_write(context, text, strlen(text)); send_pending(fd, context); sent = true; } while (nread = tls_read(context, read_buffer, sizeof(read_buffer))) { fwrite(read_buffer, nread, 1, stdout); fflush(stdout); } } } } }