aboutsummaryrefslogtreecommitdiff
path: root/lib/net.c
blob: 2bb720a4190b1163becb66639dd4c90947534678 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include "toys.h"

int xsocket(int domain, int type, int protocol)
{
  int fd = socket(domain, type, protocol);

  if (fd < 0) perror_exit("socket %x %x", type, protocol);
  fcntl(fd, F_SETFD, FD_CLOEXEC);

  return fd;
}

void xsetsockopt(int fd, int level, int opt, void *val, socklen_t len)
{
  if (-1 == setsockopt(fd, level, opt, val, len)) perror_exit("setsockopt");
}

// if !host bind to all local interfaces
struct addrinfo *xgetaddrinfo(char *host, char *port, int family, int socktype,
  int protocol, int flags)
{
  struct addrinfo info, *ai;
  int rc;

  memset(&info, 0, sizeof(struct addrinfo));
  info.ai_family = family;
  info.ai_socktype = socktype;
  info.ai_protocol = protocol;
  info.ai_flags = flags;
  if (!host) info.ai_flags |= AI_PASSIVE;

  rc = getaddrinfo(host, port, &info, &ai);
  if (rc || !ai)
    error_exit("%s%s%s: %s", host ? host : "*", port ? ":" : "",
      port ? port : "", rc ? gai_strerror(rc) : "not found");

  return ai;
}

int xconnbind(struct addrinfo *ai_arg, int dobind)
{
  struct addrinfo *ai;
  int fd = -1, one = 1;

  // Try all the returned addresses. Report errors if last entry can't connect.
  for (ai = ai_arg; ai; ai = ai->ai_next) {
    fd = (ai->ai_next ? socket : xsocket)(ai->ai_family, ai->ai_socktype,
      ai->ai_protocol);
    xsetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
    if (!(dobind ? bind : connect)(fd, ai->ai_addr, ai->ai_addrlen)) break;
    else if (!ai->ai_next) perror_exit_raw(dobind ? "bind" : "connect");
    close(fd);
  }
  freeaddrinfo(ai_arg);

  return fd;
}

int xconnect(struct addrinfo *ai)
{
  return xconnbind(ai, 0);
}


int xbind(struct addrinfo *ai)
{
  return xconnbind(ai, 1);
}

int xpoll(struct pollfd *fds, int nfds, int timeout)
{
  int i;
  long long now, then = timeout>0 ? millitime() : 0;

  for (;;) {
    if (0<=(i = poll(fds, nfds, timeout)) || toys.signal) return i;
    if (errno != EINTR && errno != ENOMEM) perror_exit("xpoll");
    else {
      now = millitime();
      timeout -= now-then;
      then = now;
    }
  }
}

// Loop forwarding data from in1 to out1 and in2 to out2, handling
// half-connection shutdown. timeouts return if no data for X ms.
// Returns 0: both closed, 1 shutdown_timeout, 2 timeout
int pollinate(int in1, int in2, int out1, int out2, int timeout, int shutdown_timeout)
{
  struct pollfd pollfds[2];
  int i, pollcount = 2;

  memset(pollfds, 0, 2*sizeof(struct pollfd));
  pollfds[0].events = pollfds[1].events = POLLIN;
  pollfds[0].fd = in1;
  pollfds[1].fd = in2;

  // Poll loop copying data from each fd to the other one.
  for (;;) {
    if (!xpoll(pollfds, pollcount, timeout)) return pollcount;

    for (i=0; i<pollcount; i++) {
      if (pollfds[i].revents & POLLIN) {
        int len = read(pollfds[i].fd, libbuf, sizeof(libbuf));
        if (len<1) pollfds[i].revents = POLLHUP;
        else xwrite(i ? out2 : out1, libbuf, len);
      }
      if (pollfds[i].revents & POLLHUP) {
        // Close half-connection.  This is needed for things like
        // "echo GET / | netcat landley.net 80"
        // Note that in1 closing triggers timeout, in2 returns now.
        if (i) {
          shutdown(pollfds[0].fd, SHUT_WR);
          pollcount--;
          timeout = shutdown_timeout;
        } else return 0;
      }
    }
  }
}

// Return converted ipv4/ipv6 numeric address in libbuf
char *ntop(struct sockaddr *sa)
{
  void *addr;

  if (sa->sa_family == AF_INET) addr = &((struct sockaddr_in *)sa)->sin_addr;
  else addr = &((struct sockaddr_in6 *)sa)->sin6_addr;

  inet_ntop(sa->sa_family, addr, libbuf, sizeof(libbuf));

  return libbuf;
}

void xsendto(int sockfd, void *buf, size_t len, struct sockaddr *dest)
{
  int rc = sendto(sockfd, buf, len, 0, dest,
    dest->sa_family == AF_INET ? sizeof(struct sockaddr_in) :
      sizeof(struct sockaddr_in6));

  if (rc != len) perror_exit("sendto");
}

// xrecvfrom with timeout in milliseconds
int xrecvwait(int fd, char *buf, int len, union socksaddr *sa, int timeout)
{
  socklen_t sl = sizeof(*sa);

  if (timeout >= 0) {
    struct pollfd pfd;

    pfd.fd = fd;
    pfd.events = POLLIN;
    if (!xpoll(&pfd, 1, timeout)) return 0;
  }

  len = recvfrom(fd, buf, len, 0, (void *)sa, &sl);
  if (len<0) perror_exit("recvfrom");

  return len;
}