master xplshn/aruu / cmd / net / netcat.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <sys/socket.h>
  4#include <sys/types.h>
  5
  6#include <arpa/inet.h>
  7#include <netinet/in.h>
  8
  9#include <errno.h>
 10#include <fcntl.h>
 11#include <netdb.h>
 12#include <poll.h>
 13#include <stdio.h>
 14#include <stdlib.h>
 15#include <string.h>
 16#include <unistd.h>
 17
 18#include "util.h"
 19
 20static void
 21usage(void)
 22{
 23  eprintf("usage: %s [-lu] [-p localport] [host] [port]\n", argv0);
 24}
 25
 26static int
 27resolve(
 28    const char              *host,
 29    const char              *port,
 30    int                      family,
 31    int                      socktype,
 32    int                      passive,
 33    struct sockaddr_storage *addr,
 34    socklen_t               *addrlen
 35)
 36{
 37  struct addrinfo hints, *res;
 38  int             r;
 39
 40  memset(&hints, 0, sizeof(hints));
 41  hints.ai_family   = family;
 42  hints.ai_socktype = socktype;
 43  if (passive)
 44    hints.ai_flags = AI_PASSIVE;
 45
 46  if ((r = getaddrinfo(host, port, &hints, &res)) != 0) {
 47    weprintf("getaddrinfo: %s\n", gai_strerror(r));
 48    return -1;
 49  }
 50
 51  memcpy(addr, res->ai_addr, res->ai_addrlen);
 52  *addrlen = res->ai_addrlen;
 53  freeaddrinfo(res);
 54  return 0;
 55}
 56
 57// ?man netcat: read and write data across network connections
 58// ?man arguments: host [port]
 59// ?man arbitrary data transmission over tcp or udp
 60int
 61main(int argc, char *argv[])
 62{
 63  struct sockaddr_storage local_addr, remote_addr;
 64  socklen_t               local_len = sizeof(local_addr), remote_len = sizeof(remote_addr);
 65  struct pollfd           fds[2];
 66  int                     listenfd = -1, sockfd = -1;
 67  int                     lflag      = 0;
 68  int                     uflag      = 0;
 69  char                   *port       = NULL;
 70  char                   *host       = NULL;
 71  char                   *local_port = NULL;
 72  int                     socktype;
 73  int                     n, opt;
 74  char                    buf[BUFSIZ];
 75
 76  ARGBEGIN
 77  {
 78    // ?man -l: list in long format
 79    case 'l':
 80      lflag = 1;
 81      break;
 82    // ?man -p:str: preserve file attributes
 83    case 'p':
 84      local_port = EARGF(usage());
 85      break;
 86    // ?man -u: unbuffered output
 87    case 'u':
 88      uflag = 1;
 89      break;
 90    default:
 91      usage();
 92  }
 93  ARGEND
 94
 95  socktype = uflag ? SOCK_DGRAM : SOCK_STREAM;
 96
 97  if (lflag) {
 98    /* server mode */
 99    if (!local_port) {
100      if (argc == 1) {
101        local_port = argv[0];
102        argc       = 0;
103      } else {
104        usage();
105      }
106    }
107    memset(&local_addr, 0, sizeof(local_addr));
108    if (resolve(NULL, local_port, AF_UNSPEC, socktype, 1, &local_addr, &local_len) < 0)
109      return 1;
110
111    listenfd = socket(local_addr.ss_family, socktype, 0);
112    if (listenfd < 0)
113      eprintf("socket:");
114
115    opt = 1;
116    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
117
118    if (bind(listenfd, (struct sockaddr *)&local_addr, local_len) < 0)
119      eprintf("bind:");
120
121    if (!uflag) {
122      if (listen(listenfd, 5) < 0)
123        eprintf("listen:");
124      sockfd = accept(listenfd, (struct sockaddr *)&remote_addr, &remote_len);
125      if (sockfd < 0)
126        eprintf("accept:");
127      close(listenfd);
128    } else {
129      sockfd = listenfd;
130      n      = recvfrom(
131          sockfd, buf, sizeof(buf), MSG_PEEK, (struct sockaddr *)&remote_addr, &remote_len
132      );
133      if (n < 0)
134        eprintf("recvfrom:");
135      if (connect(sockfd, (struct sockaddr *)&remote_addr, remote_len) < 0)
136        eprintf("connect:");
137    }
138  } else {
139    /* client mode */
140    if (argc != 2)
141      usage();
142    host = argv[0];
143    port = argv[1];
144
145    memset(&remote_addr, 0, sizeof(remote_addr));
146    if (resolve(host, port, AF_UNSPEC, socktype, 0, &remote_addr, &remote_len) < 0)
147      return 1;
148
149    sockfd = socket(remote_addr.ss_family, socktype, 0);
150    if (sockfd < 0)
151      eprintf("socket:");
152
153    if (local_port) {
154      memset(&local_addr, 0, sizeof(local_addr));
155      if (resolve(NULL, local_port, remote_addr.ss_family, socktype, 1, &local_addr, &local_len)
156          < 0)
157        return 1;
158      opt = 1;
159      setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
160      if (bind(sockfd, (struct sockaddr *)&local_addr, local_len) < 0)
161        eprintf("bind:");
162    }
163
164    if (connect(sockfd, (struct sockaddr *)&remote_addr, remote_len) < 0)
165      eprintf("connect:");
166  }
167
168  fds[0].fd     = 0;
169  fds[0].events = POLLIN;
170  fds[1].fd     = sockfd;
171  fds[1].events = POLLIN;
172
173  while (1) {
174    if (poll(fds, 2, -1) < 0) {
175      if (errno == EINTR)
176        continue;
177      eprintf("poll:");
178    }
179
180    if (fds[0].revents & POLLIN) {
181      n = read(0, buf, sizeof(buf));
182      if (n < 0) {
183        weprintf("read stdin:");
184        break;
185      }
186      if (n == 0) {
187        if (!uflag) {
188          shutdown(sockfd, SHUT_WR);
189          fds[0].fd = -1;
190        } else {
191          break;
192        }
193      } else {
194        if (writeall(sockfd, buf, n) < 0) {
195          weprintf("write socket:");
196          break;
197        }
198      }
199    }
200
201    if (fds[1].revents & POLLIN) {
202      n = read(sockfd, buf, sizeof(buf));
203      if (n < 0) {
204        weprintf("read socket:");
205        break;
206      }
207      if (n == 0) {
208        break;
209      } else {
210        if (writeall(1, buf, n) < 0) {
211          weprintf("write stdout:");
212          break;
213        }
214      }
215    }
216
217    if ((fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
218        || (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))) {
219      break;
220    }
221  }
222
223  close(sockfd);
224  return 0;
225}