#include <mLib/alloc.h>
#include <mLib/bits.h>
+#include <mLib/darray.h>
#include <mLib/dstr.h>
#include <mLib/fdflags.h>
#include <mLib/mdwopt.h>
struct sockaddr_in sin;
} addr;
+DA_DECL(addr_v, addr);
+DA_DECL(str_v, const char *);
+
typedef struct pk {
struct pk *next; /* Next packet in the chain */
octet *p, *o; /* Buffer start and current posn */
typedef struct connwait {
unsigned f; /* Various flags */
#define cwf_port 1u /* Port is defined => listen */
- sel_file a; /* Selector */
- addr me, peer; /* Who I'm meant to be; who peer is */
+ sel_file *sfv; /* Selectors */
+ addr_v me, peer; /* Who I'm meant to be; who peer is */
} connwait;
/*----- Static variables --------------------------------------------------*/
static int cloexec(int fd)
{ return (fdflags(fd, 0, 0, FD_CLOEXEC, FD_CLOEXEC)); }
+static socklen_t addrsz(const addr *a)
+{
+ switch (a->sa.sa_family) {
+ case AF_INET: return sizeof(a->sin);
+ default: abort();
+ }
+}
+
+static const char *addrstr(const addr *a)
+{
+ static char buf[128];
+ socklen_t n = sizeof(buf);
+
+ if (getnameinfo(&a->sa, addrsz(a), buf, n, 0, 0, NI_NUMERICHOST))
+ return ("<addrstr failed>");
+ return (buf);
+}
+
+static int addreq(const addr *a, const addr *b)
+{
+ if (a->sa.sa_family != b->sa.sa_family) return (0);
+ switch (a->sa.sa_family) {
+ case AF_INET:
+ return (a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr);
+ default:
+ abort();
+ }
+}
+
static void initaddr(addr *a)
{
a->sin.sin_family = AF_INET;
int fd;
addr a;
socklen_t sz = sizeof(a);
+ size_t i, n;
if ((fd = accept(fd_s, &a.sa, &sz)) < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) return;
moan("couldn't accept incoming connection: %s", strerror(errno));
return;
}
- if (cw.peer.sin.sin_addr.s_addr != INADDR_ANY &&
- cw.peer.sin.sin_addr.s_addr != a.sin.sin_addr.s_addr) {
- moan("rejecting connection from %s", inet_ntoa(a.sin.sin_addr));
- close(fd); return;
- }
+ n = DA_LEN(&cw.peer);
+ if (!n) goto match;
+ for (i = 0; i < n; i++) if (addreq(&a, &DA(&cw.peer)[i])) goto match;
+ moan("rejecting connection from %s", addrstr(&a));
+ close(fd); return;
+match:
if (nonblockify(fd) || cloexec(fd)) {
moan("couldn't accept incoming connection: %s", strerror(errno));
close(fd); return;
}
dofwd(fd, fd);
- close(fd_s);
- sel_rmfile(&cw.a);
+ n = DA_LEN(&cw.me);
+ for (i = 0; i < n; i++) { close(cw.sfv[i].fd); sel_rmfile(&cw.sfv[i]); }
}
-static void dolisten(void)
+static void dolisten1(const addr *a, sel_file *sf)
{
int fd;
int opt = 1;
- if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0 ||
+ if ((fd = socket(a->sa.sa_family, SOCK_STREAM, IPPROTO_TCP)) < 0 ||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) ||
- bind(fd, &cw.me.sa, sizeof(cw.me.sin)) ||
+ bind(fd, &a->sa, addrsz(a)) ||
listen(fd, 1) || nonblockify(fd) || cloexec(fd))
die(1, "couldn't set up listening socket: %s", strerror(errno));
- sel_initfile(&sel, &cw.a, fd, SEL_READ, doaccept, 0);
- sel_addfile(&cw.a);
+ sel_initfile(&sel, sf, fd, SEL_READ, doaccept, 0);
+ sel_addfile(sf);
+}
+
+static void dolisten(void)
+{
+ size_t i, n;
+
+ n = DA_LEN(&cw.me);
+ for (i = 0; i < n; i++)
+ dolisten1(&DA(&cw.me)[i], &cw.sfv[i]);
+}
+
+static void pushaddr(addr_v *av, const addr *a)
+{
+ DA_ENSURE(av, 1);
+ DA(av)[DA_LEN(av)] = *a;
+ DA_EXTEND(av, 1);
}
#define paf_parse 1u
int main(int argc, char *argv[])
{
unsigned f = 0;
- const char *bindhost = 0, *bindsvc = 0, *peerhost = 0;
+ str_v bindhosts = DA_INIT, peerhosts = DA_INIT;
+ const char *bindsvc = 0;
addr bindaddr;
const char *connhost = 0;
addr tmpaddr;
int fd = -1;
int len = 65536;
+ size_t i, n;
#define f_bogus 1u
case 'v': version(stdout); exit(0);
case 'u': usage(stdout); exit(0);
case 'l': bindsvc = optarg; break;
- case 'p': peerhost = optarg; break;
- case 'b': bindhost = optarg; break;
+ case 'p': DA_PUSH(&peerhosts, optarg); break;
+ case 'b': DA_PUSH(&bindhosts, optarg); break;
case 'c': connhost = optarg; break;
default: f |= f_bogus; break;
}
}
if (optind + 2 != argc || (f&f_bogus)) { usage(stderr); exit(1); }
- if (bindhost && !bindsvc && !connhost)
+ if (DA_LEN(&bindhosts) && !bindsvc && !connhost)
die(1, "bind addr only makes sense when listening or connecting");
- if (peerhost && !bindsvc)
+ if (DA_LEN(&peerhosts) && !bindsvc)
die(1, "peer addr only makes sense when listening");
if (bindsvc && connhost)
die(1, "can't listen and connect");
- if (bindhost || bindsvc) {
- initaddr(&bindaddr);
- if (!bindsvc) parseaddr(bindhost, 0, 0, &bindaddr);
- else {
- initaddr(&cw.me);
- parseaddr(bindhost, bindsvc, 0, &cw.me);
+ DA_CREATE(&cw.me); DA_CREATE(&cw.peer);
+
+ n = DA_LEN(&bindhosts);
+ if (n || bindsvc) {
+ if (!n) {
+ initaddr(&tmpaddr);
+ parseaddr(0, bindsvc, 0, &tmpaddr);
+ pushaddr(&cw.me, &tmpaddr);
+ } else if (!bindsvc) {
+ if (n != 1) die(1, "can only bind to one address as client");
+ initaddr(&bindaddr);
+ parseaddr(DA(&bindhosts)[0], 0, 0, &bindaddr);
+ } else for (i = 0; i < n; i++) {
+ initaddr(&tmpaddr);
+ parseaddr(DA(&bindhosts)[i], bindsvc, 0, &tmpaddr);
+ pushaddr(&cw.me, &tmpaddr);
+ }
+ if (bindsvc) {
cw.f |= cwf_port;
+ n = DA_LEN(&cw.me);
+ cw.sfv = xmalloc(n*sizeof(*cw.sfv));
}
}
- initaddr(&cw.peer);
- if (peerhost) parseaddr(peerhost, 0, 0, &cw.peer);
+ n = DA_LEN(&peerhosts);
+ if (n) {
+ for (i = 0; i < n; i++) {
+ initaddr(&tmpaddr);
+ parseaddr(DA(&peerhosts)[0], 0, 0, &tmpaddr);
+ pushaddr(&cw.peer, &tmpaddr);
+ }
+ }
if (connhost) {
initaddr(&tmpaddr);
parseaddr(connhost, 0, paf_parse, &tmpaddr);
- if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0 ||
- (bindhost &&
- bind(fd, &bindaddr.sa, sizeof(bindaddr.sin))) ||
- connect(fd, &tmpaddr.sa, sizeof(tmpaddr.sin)))
+ if ((fd = socket(tmpaddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP)) < 0 ||
+ (DA_LEN(&bindhosts) &&
+ bind(fd, &bindaddr.sa, addrsz(&bindaddr))) ||
+ connect(fd, &tmpaddr.sa, addrsz(&tmpaddr)))
die(1, "couldn't connect to TCP server: %s", strerror(errno));
if (nonblockify(fd) || cloexec(fd))
die(1, "couldn't connect to TCP server: %s", strerror(errno));
initaddr(&tmpaddr);
parseaddr(argv[optind], 0, paf_parse, &tmpaddr);
- if ((fd_udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 ||
+ if ((fd_udp = socket(tmpaddr.sa.sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0 ||
nonblockify(fd_udp) || cloexec(fd_udp) ||
setsockopt(fd_udp, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) ||
setsockopt(fd_udp, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len)) ||
- bind(fd_udp, &tmpaddr.sa, sizeof(tmpaddr.sin)))
+ bind(fd_udp, &tmpaddr.sa, addrsz(&tmpaddr)))
die(1, "couldn't set up UDP socket: %s", strerror(errno));
initaddr(&tmpaddr);
parseaddr(argv[optind + 1], 0, paf_parse, &tmpaddr);
- if (connect(fd_udp, &tmpaddr.sa, sizeof(tmpaddr.sin)))
+ if (connect(fd_udp, &tmpaddr.sa, addrsz(&tmpaddr)))
die(1, "couldn't set up UDP socket: %s", strerror(errno));
if (bindsvc) dolisten();