/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include static int tcp_connect(const char *host, const char *port) { struct addrinfo hints; struct addrinfo *result, *rp; int s; int fd; int err; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_protocol = IPPROTO_TCP; s = getaddrinfo(host, port, &hints, &result); if (s < 0) errx(1, "unable to resolve host: %s", gai_strerror(s)); for (rp = result; rp; rp = rp->ai_next) { fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (fd < 0) continue; if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) break; err = errno; close(fd); errno = err; } if (rp == NULL) fd = -1; freeaddrinfo(result); return fd; } static int write_all(int fd, const void *buf, size_t count) { ssize_t nbytes; while (count > 0) { nbytes = write(fd, buf, count); if (nbytes < 0 && errno == EINTR) continue; if (nbytes < 0) return -1; buf += nbytes; count -= nbytes; } return 0; } static void usage(int status) { fputs("usage: electrumc
[]\n", status ? stderr : stdout); exit(status); } static int read_response(int s, SSL *ssl, bool use_ssl) { char buf[8192]; int len; if (use_ssl) { len = SSL_pending(ssl); if (len < 0) errx(1, "ssl pending"); if (len == 0) len = 1; else if (len > (int)sizeof(buf)) len = sizeof(buf); len = SSL_read(ssl, buf, len); if (len < 0) errx(1, "ssl read failed"); } else { len = read(s, buf, sizeof(buf)); if (len == 0) return -1; if (len < 0) err(1, "read"); } len = fwrite(buf, 1, len, stdout); if (len < 0) errx(1, "failed to print response"); if (buf[len-1] == '\n') return 1; return 0; } int main(int argc, char *argv[]) { int rc; SSL *ssl; SSL_CTX *ssl_ctx; char *host, *port, *type, *method, *params; int s; bool use_ssl; if (argc == 1) usage(0); if (argc != 3 && argc != 4) usage(1); host = argv[1]; method = argv[2]; params = argc == 3 ? NULL : argv[3]; type = strrchr(host, ':'); if (!type) errx(1, "address should be in format ::"); *type++ = '\0'; if (!strcmp(type, "t")) use_ssl = false; else if (!strcmp(type, "s")) use_ssl = true; else errx(1, "address type must be 't' or 's', but found: '%s'", type); port = strrchr(host, ':'); if (!port) errx(1, "address should be in format ::"); *port++ = '\0'; if (!*host) errx(1, "address hostname missing"); if (use_ssl) { ssl_ctx = SSL_CTX_new(TLS_client_method()); if (ssl_ctx == NULL) errx(1, "failed to create ssl context"); (void)SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); ssl = SSL_new(ssl_ctx); if (ssl_ctx == NULL) errx(1, "failed to create ssl object"); } s = tcp_connect(host, port); if (s < 0) err(1, "failed to connect"); if (use_ssl) { rc = SSL_set_fd(ssl, s); if (rc < 0) errx(1, "failed to set ssl s"); rc = SSL_connect(ssl); if (rc < 0) errx(1, "ssl_connect failed"); } char buf[8192]; int len = snprintf(buf, sizeof(buf)-1, "{\"id\":0,\"method\":\"%s\"%s%s}\n", method, params ? ",\"params\":" : "", params ? params : ""); if (len < 0 || len == (int)sizeof(buf)) errx(1, "request too long"); if (use_ssl) { rc = SSL_write(ssl, buf, len); if (rc < 0) errx(1, "ssl write failed"); } else { len = write_all(s, buf, len); if (len < 0) err(1, "write"); } do rc = read_response(s, ssl, use_ssl); while (rc == 0); if (rc < 0 && errno == 0) errx(1, "incomplete response"); if (rc < 0) err(1, "read"); if (use_ssl) { rc = SSL_shutdown(ssl); if (rc < 0) errx(1, "ssl shutdown failed"); } close(s); }