diff options
Diffstat (limited to 'netcat.c')
| -rw-r--r-- | netcat.c | 244 |
1 files changed, 156 insertions, 88 deletions
@@ -1,4 +1,4 @@ -/* $OpenBSD: netcat.c,v 1.203 2019/02/26 17:32:47 jsing Exp $ */ +/* $OpenBSD: netcat.c,v 1.226 2023/08/14 08:07:27 tb Exp $ */ /* * Copyright (c) 2001 Eric Jackson <ericj@monkey.org> * Copyright (c) 2015 Bob Beck. All rights reserved. @@ -103,7 +103,7 @@ const char *Kflag; /* Private key file */ const char *oflag; /* OCSP stapling file */ const char *Rflag; /* Root CA file */ int tls_cachanged; /* Using non-default CA file */ -int TLSopt; /* TLS options */ +int TLSopt; /* TLS options */ char *tls_expectname; /* required name in peer cert */ char *tls_expecthash; /* required hash of peer cert */ char *tls_ciphers; /* TLS ciphers */ @@ -125,12 +125,13 @@ void help(void) __attribute__((noreturn)); int local_listen(const char *, const char *, struct addrinfo); void readwrite(int, struct tls *); void fdpass(int nfd) __attribute__((noreturn)); -int remote_connect(const char *, const char *, struct addrinfo); +int remote_connect(const char *, const char *, struct addrinfo, char *); int timeout_tls(int, struct tls *, int (*)(struct tls *)); int timeout_connect(int, const struct sockaddr *, socklen_t); int socks_connect(const char *, const char *, struct addrinfo, const char *, const char *, struct addrinfo, int, const char *); int udptest(int); +void connection_info(const char *, const char *, const char *, const char *); int unix_bind(char *, int); int unix_connect(char *); int unix_listen(char *); @@ -151,8 +152,8 @@ main(int argc, char *argv[]) { int ch, s = -1, ret, socksv; char *host, *uport; + char ipaddr[NI_MAXHOST]; struct addrinfo hints; - struct servent *sv; socklen_t len; struct sockaddr_storage cliaddr; char *proxy = NULL, *proxyport = NULL; @@ -167,7 +168,6 @@ main(int argc, char *argv[]) socksv = 5; host = NULL; uport = NULL; - sv = NULL; Rflag = tls_default_ca_cert_file(); signal(SIGPIPE, SIG_IGN); @@ -351,15 +351,11 @@ main(int argc, char *argv[]) err(1, "setrtable"); /* Cruft to make sure options are clean, and used properly. */ - if (argv[0] && !argv[1] && family == AF_UNIX) { + if (argc == 1 && family == AF_UNIX) { host = argv[0]; - uport = NULL; - } else if (argv[0] && !argv[1]) { - if (!lflag) - usage(1); + } else if (argc == 1 && lflag) { uport = argv[0]; - host = NULL; - } else if (argv[0] && argv[1]) { + } else if (argc == 2) { host = argv[0]; uport = argv[1]; } else @@ -367,24 +363,36 @@ main(int argc, char *argv[]) if (usetls) { if (Cflag && unveil(Cflag, "r") == -1) - err(1, "unveil"); + err(1, "unveil %s", Cflag); if (unveil(Rflag, "r") == -1) - err(1, "unveil"); + err(1, "unveil %s", Rflag); if (Kflag && unveil(Kflag, "r") == -1) - err(1, "unveil"); + err(1, "unveil %s", Kflag); if (oflag && unveil(oflag, "r") == -1) - err(1, "unveil"); + err(1, "unveil %s", oflag); + } else if (family == AF_UNIX && uflag && lflag && !kflag) { + /* + * After recvfrom(2) from client, the server connects + * to the client socket. As the client path is determined + * during runtime, we cannot unveil(2). + */ } else { if (family == AF_UNIX) { if (unveil(host, "rwc") == -1) - err(1, "unveil"); - if (uflag && !lflag) { - if (unveil(sflag ? sflag : "/tmp", "rwc") == -1) - err(1, "unveil"); + err(1, "unveil %s", host); + if (uflag && !kflag) { + if (sflag) { + if (unveil(sflag, "rwc") == -1) + err(1, "unveil %s", sflag); + } else { + if (unveil("/tmp", "rwc") == -1) + err(1, "unveil /tmp"); + } } } else { + /* no filesystem visibility */ if (unveil("/", "") == -1) - err(1, "unveil"); + err(1, "unveil /"); } } @@ -568,9 +576,13 @@ main(int argc, char *argv[]) close(s); s = local_listen(host, uport, hints); } - if (s < 0) + if (s == -1) err(1, NULL); if (uflag && kflag) { + if (family == AF_UNIX) { + if (pledge("stdio unix", NULL) == -1) + err(1, "pledge"); + } /* * For UDP and -k, don't connect the socket, * let it receive datagrams from multiple @@ -590,16 +602,21 @@ main(int argc, char *argv[]) len = sizeof(z); rv = recvfrom(s, buf, sizeof(buf), MSG_PEEK, (struct sockaddr *)&z, &len); - if (rv < 0) + if (rv == -1) err(1, "recvfrom"); rv = connect(s, (struct sockaddr *)&z, len); - if (rv < 0) + if (rv == -1) err(1, "connect"); + if (family == AF_UNIX) { + if (pledge("stdio unix", NULL) == -1) + err(1, "pledge"); + } if (vflag) report_sock("Connection received", - (struct sockaddr *)&z, len, NULL); + (struct sockaddr *)&z, len, + family == AF_UNIX ? host : NULL); readwrite(s, NULL); } else { @@ -628,7 +645,7 @@ main(int argc, char *argv[]) tls_free(tls_cctx); } if (family == AF_UNIX && uflag) { - if (connect(s, NULL, 0) < 0) + if (connect(s, NULL, 0) == -1) err(1, "connect"); } @@ -650,7 +667,6 @@ main(int argc, char *argv[]) if (uflag) unlink(unix_dg_tmp_socket); return ret; - } else { int i = 0; @@ -676,35 +692,27 @@ main(int argc, char *argv[]) proxy, proxyport, proxyhints, socksv, Pflag); else - s = remote_connect(host, portlist[i], hints); + s = remote_connect(host, portlist[i], hints, + ipaddr); if (s == -1) continue; ret = 0; if (vflag || zflag) { + int print_info = 1; + /* For UDP, make sure we are connected. */ if (uflag) { - if (udptest(s) == -1) { + /* No info on failed or skipped test. */ + if ((print_info = udptest(s)) == -1) { ret = 1; continue; } } - - /* Don't look up port if -n. */ - if (nflag) - sv = NULL; - else { - sv = getservbyport( - ntohs(atoi(portlist[i])), - uflag ? "udp" : "tcp"); - } - - fprintf(stderr, - "Connection to %s %s port [%s/%s] " - "succeeded!\n", host, portlist[i], - uflag ? "udp" : "tcp", - sv ? sv->s_name : "*"); + if (print_info == 1) + connection_info(host, portlist[i], + uflag ? "udp" : "tcp", ipaddr); } if (Fflag) fdpass(s); @@ -739,7 +747,7 @@ unix_bind(char *path, int flags) /* Create unix domain socket. */ if ((s = socket(AF_UNIX, flags | (uflag ? SOCK_DGRAM : SOCK_STREAM), - 0)) < 0) + 0)) == -1) return -1; memset(&s_un, 0, sizeof(struct sockaddr_un)); @@ -752,7 +760,7 @@ unix_bind(char *path, int flags) return -1; } - if (bind(s, (struct sockaddr *)&s_un, sizeof(s_un)) < 0) { + if (bind(s, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { save_errno = errno; close(s); errno = save_errno; @@ -797,7 +805,7 @@ tls_setup_client(struct tls *tls_ctx, int s, char *host) const char *errstr; if (tls_connect_socket(tls_ctx, s, - tls_expectname ? tls_expectname : host) == -1) { + tls_expectname ? tls_expectname : host) == -1) { errx(1, "tls connection failed (%s)", tls_error(tls_ctx)); } @@ -808,8 +816,8 @@ tls_setup_client(struct tls *tls_ctx, int s, char *host) } if (vflag) report_tls(tls_ctx, host); - if (tls_expecthash && tls_peer_cert_hash(tls_ctx) && - strcmp(tls_expecthash, tls_peer_cert_hash(tls_ctx)) != 0) + if (tls_expecthash && (tls_peer_cert_hash(tls_ctx) == NULL || + strcmp(tls_expecthash, tls_peer_cert_hash(tls_ctx)) != 0)) errx(1, "peer certificate is not %s", tls_expecthash); if (Zflag) { save_peer_cert(tls_ctx, Zflag); @@ -837,8 +845,9 @@ tls_setup_server(struct tls *tls_ctx, int connfd, char *host) report_tls(tls_cctx, host); if ((TLSopt & TLS_CCERT) && !gotcert) warnx("No client certificate provided"); - else if (gotcert && tls_peer_cert_hash(tls_ctx) && tls_expecthash && - strcmp(tls_expecthash, tls_peer_cert_hash(tls_ctx)) != 0) + else if (gotcert && tls_expecthash && + (tls_peer_cert_hash(tls_cctx) == NULL || + strcmp(tls_expecthash, tls_peer_cert_hash(tls_cctx)) != 0)) warnx("peer certificate is not %s", tls_expecthash); else if (gotcert && tls_expectname && (!tls_peer_cert_contains_name(tls_cctx, tls_expectname))) @@ -862,10 +871,10 @@ unix_connect(char *path) int s, save_errno; if (uflag) { - if ((s = unix_bind(unix_dg_tmp_socket, SOCK_CLOEXEC)) < 0) + if ((s = unix_bind(unix_dg_tmp_socket, SOCK_CLOEXEC)) == -1) return -1; } else { - if ((s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0) + if ((s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) return -1; } @@ -878,14 +887,13 @@ unix_connect(char *path) errno = ENAMETOOLONG; return -1; } - if (connect(s, (struct sockaddr *)&s_un, sizeof(s_un)) < 0) { + if (connect(s, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { save_errno = errno; close(s); errno = save_errno; return -1; } return s; - } /* @@ -897,9 +905,9 @@ unix_listen(char *path) { int s; - if ((s = unix_bind(path, 0)) < 0) + if ((s = unix_bind(path, 0)) == -1) return -1; - if (listen(s, 5) < 0) { + if (listen(s, 5) == -1) { close(s); return -1; } @@ -915,10 +923,11 @@ unix_listen(char *path) * port or source address if needed. Returns -1 on failure. */ int -remote_connect(const char *host, const char *port, struct addrinfo hints) +remote_connect(const char *host, const char *port, struct addrinfo hints, + char *ipaddr) { struct addrinfo *res, *res0; - int s = -1, error, on = 1, save_errno; + int s = -1, error, herr, on = 1, save_errno; if ((error = getaddrinfo(host, port, &hints, &res0))) errx(1, "getaddrinfo for host \"%s\" port %s: %s", host, @@ -926,7 +935,7 @@ remote_connect(const char *host, const char *port, struct addrinfo hints) for (res = res0; res; res = res->ai_next) { if ((s = socket(res->ai_family, res->ai_socktype | - SOCK_NONBLOCK, res->ai_protocol)) < 0) + SOCK_NONBLOCK, res->ai_protocol)) == -1) continue; /* Bind to a local port or source address if specified. */ @@ -944,18 +953,39 @@ remote_connect(const char *host, const char *port, struct addrinfo hints) errx(1, "getaddrinfo: %s", gai_strerror(error)); if (bind(s, (struct sockaddr *)ares->ai_addr, - ares->ai_addrlen) < 0) + ares->ai_addrlen) == -1) err(1, "bind failed"); freeaddrinfo(ares); } set_common_sockopts(s, res->ai_family); + if (ipaddr != NULL) { + herr = getnameinfo(res->ai_addr, res->ai_addrlen, + ipaddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + switch (herr) { + case 0: + break; + case EAI_SYSTEM: + err(1, "getnameinfo"); + default: + errx(1, "getnameinfo: %s", gai_strerror(herr)); + } + } + if (timeout_connect(s, res->ai_addr, res->ai_addrlen) == 0) break; - if (vflag) - warn("connect to %s port %s (%s) failed", host, port, - uflag ? "udp" : "tcp"); + + if (vflag) { + /* only print IP if there is something to report */ + if (nflag || ipaddr == NULL || + (strncmp(host, ipaddr, NI_MAXHOST) == 0)) + warn("connect to %s port %s (%s) failed", host, + port, uflag ? "udp" : "tcp"); + else + warn("connect to %s (%s) port %s (%s) failed", + host, ipaddr, port, uflag ? "udp" : "tcp"); + } save_errno = errno; close(s); @@ -1023,7 +1053,7 @@ local_listen(const char *host, const char *port, struct addrinfo hints) for (res = res0; res; res = res->ai_next) { if ((s = socket(res->ai_family, res->ai_socktype, - res->ai_protocol)) < 0) + res->ai_protocol)) == -1) continue; ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof(x)); @@ -1043,7 +1073,7 @@ local_listen(const char *host, const char *port, struct addrinfo hints) } if (!uflag && s != -1) { - if (listen(s, 1) < 0) + if (listen(s, 1) == -1) err(1, "listen"); } if (vflag && s != -1) { @@ -1147,7 +1177,7 @@ readwrite(int net_fd, struct tls *tls_ctx) pfd[POLL_NETIN].fd = -1; if (pfd[POLL_NETOUT].revents & POLLHUP) { - if (Nflag) + if (pfd[POLL_NETOUT].fd != -1 && Nflag) shutdown(pfd[POLL_NETOUT].fd, SHUT_WR); pfd[POLL_NETOUT].fd = -1; } @@ -1226,7 +1256,7 @@ readwrite(int net_fd, struct tls *tls_ctx) if (netinbufpos == BUFSIZE) pfd[POLL_NETIN].events = 0; /* handle telnet */ - if (tflag) + if (pfd[POLL_NETIN].fd != -1 && tflag) atelnet(pfd[POLL_NETIN].fd, netinbuf, netinbufpos); } @@ -1267,6 +1297,9 @@ drainbuf(int fd, unsigned char *buf, size_t *bufpos, struct tls *tls) ssize_t n; ssize_t adjust; + if (fd == -1) + return -1; + if (tls) { n = tls_write(tls, buf, *bufpos); if (n == -1) @@ -1293,6 +1326,9 @@ fillbuf(int fd, unsigned char *buf, size_t *bufpos, struct tls *tls) size_t num = BUFSIZE - *bufpos; ssize_t n; + if (fd == -1) + return -1; + if (tls) { n = tls_read(tls, buf + *bufpos, num); if (n == -1) @@ -1399,7 +1435,6 @@ atelnet(int nfd, unsigned char *buf, unsigned int size) } } - int strtoport(char *portstr, int udp) { @@ -1453,12 +1488,12 @@ build_ports(char *p) for (x = 0; x <= hi - lo; x++) { cp = arc4random_uniform(x + 1); portlist[x] = portlist[cp]; - if (asprintf(&portlist[cp], "%d", x + lo) < 0) + if (asprintf(&portlist[cp], "%d", x + lo) == -1) err(1, "asprintf"); } } else { /* Load ports sequentially. */ for (cp = lo; cp <= hi; cp++) { - if (asprintf(&portlist[x], "%d", cp) < 0) + if (asprintf(&portlist[x], "%d", cp) == -1) err(1, "asprintf"); x++; } @@ -1484,6 +1519,10 @@ udptest(int s) { int i, ret; + /* Only write to the socket in scan mode or interactive mode. */ + if (!zflag && !isatty(STDIN_FILENO)) + return 0; + for (i = 0; i <= 3; i++) { if (write(s, "X", 1) == 1) ret = 1; @@ -1494,18 +1533,44 @@ udptest(int s) } void +connection_info(const char *host, const char *port, const char *proto, + const char *ipaddr) +{ + struct servent *sv; + char *service = "*"; + + /* Look up service name unless -n. */ + if (!nflag) { + sv = getservbyport(ntohs(atoi(port)), proto); + if (sv != NULL) + service = sv->s_name; + } + + fprintf(stderr, "Connection to %s", host); + + /* + * if we aren't connecting thru a proxy and + * there is something to report, print IP + */ + if (!nflag && !xflag && strcmp(host, ipaddr) != 0) + fprintf(stderr, " (%s)", ipaddr); + + fprintf(stderr, " %s port [%s/%s] succeeded!\n", port, proto, service); +} + +void set_common_sockopts(int s, int af) { int x = 1; if (Sflag) { if (setsockopt(s, IPPROTO_TCP, TCP_MD5SIG, - &x, sizeof(x)) == -1) + &x, sizeof(x)) == -1) err(1, NULL); } if (Dflag) { if (setsockopt(s, SOL_SOCKET, SO_DEBUG, - &x, sizeof(x)) == -1) + &x, sizeof(x)) == -1) err(1, NULL); } if (Tflag != -1) { @@ -1654,7 +1719,7 @@ save_peer_cert(struct tls *tls_ctx, FILE *fp) } void -report_tls(struct tls * tls_ctx, char * host) +report_tls(struct tls *tls_ctx, char *host) { time_t t; const char *ocsp_url; @@ -1682,7 +1747,7 @@ report_tls(struct tls * tls_ctx, char * host) switch (tls_peer_ocsp_response_status(tls_ctx)) { case TLS_OCSP_RESPONSE_SUCCESSFUL: fprintf(stderr, "OCSP Stapling: %s\n", - tls_peer_ocsp_result(tls_ctx) == NULL ? "" : + tls_peer_ocsp_result(tls_ctx) == NULL ? "" : tls_peer_ocsp_result(tls_ctx)); fprintf(stderr, " response_status=%d cert_status=%d crl_reason=%d\n", @@ -1692,22 +1757,22 @@ report_tls(struct tls * tls_ctx, char * host) t = tls_peer_ocsp_this_update(tls_ctx); fprintf(stderr, " this update: %s", t != -1 ? ctime(&t) : "\n"); - t = tls_peer_ocsp_next_update(tls_ctx); + t = tls_peer_ocsp_next_update(tls_ctx); fprintf(stderr, " next update: %s", t != -1 ? ctime(&t) : "\n"); - t = tls_peer_ocsp_revocation_time(tls_ctx); + t = tls_peer_ocsp_revocation_time(tls_ctx); fprintf(stderr, " revocation: %s", t != -1 ? ctime(&t) : "\n"); break; case -1: break; default: - fprintf(stderr, "OCSP Stapling: failure - response_status %d (%s)\n", + fprintf(stderr, + "OCSP Stapling: failure - response_status %d (%s)\n", tls_peer_ocsp_response_status(tls_ctx), - tls_peer_ocsp_result(tls_ctx) == NULL ? "" : + tls_peer_ocsp_result(tls_ctx) == NULL ? "" : tls_peer_ocsp_result(tls_ctx)); break; - } } @@ -1727,12 +1792,15 @@ report_sock(const char *msg, const struct sockaddr *sa, socklen_t salen, if (nflag) flags |= NI_NUMERICHOST; - if ((herr = getnameinfo(sa, salen, host, sizeof(host), - port, sizeof(port), flags)) != 0) { - if (herr == EAI_SYSTEM) - err(1, "getnameinfo"); - else - errx(1, "getnameinfo: %s", gai_strerror(herr)); + herr = getnameinfo(sa, salen, host, sizeof(host), port, sizeof(port), + flags); + switch (herr) { + case 0: + break; + case EAI_SYSTEM: + err(1, "getnameinfo"); + default: + errx(1, "getnameinfo: %s", gai_strerror(herr)); } fprintf(stderr, "%s on %s %s\n", msg, host, port); @@ -1769,7 +1837,7 @@ help(void) \t-R CAfile CA bundle\n\ \t-r Randomize remote ports\n\ \t-S Enable the TCP MD5 signature option\n\ - \t-s source Local source address\n\ + \t-s sourceaddr Local source address\n\ \t-T keyword TOS value or TLS options\n\ \t-t Answer TELNET negotiation\n\ \t-U Use UNIX domain socket\n\ @@ -1795,7 +1863,7 @@ usage(int ret) "\t [-i interval] [-K keyfile] [-M ttl] [-m minttl] [-O length]\n" "\t [-o staplefile] [-P proxy_username] [-p source_port] " "[-R CAfile]\n" - "\t [-s source] [-T keyword] [-V rtable] [-W recvlimit] " + "\t [-s sourceaddr] [-T keyword] [-V rtable] [-W recvlimit] " "[-w timeout]\n" "\t [-X proxy_protocol] [-x proxy_address[:port]] " "[-Z peercertfile]\n" |
