tcpsock tcpconnect(ipaddr addr, int64_t deadline) { /* Open a socket. */ int s = socket(mill_ipfamily(addr), SOCK_STREAM, 0); if(s == -1) return NULL; mill_tcptune(s); /* Connect to the remote endpoint. */ int rc = connect(s, (struct sockaddr*)&addr, mill_iplen(addr)); if(rc != 0) { mill_assert(rc == -1); if(errno != EINPROGRESS) return NULL; rc = fdwait(s, FDW_OUT, deadline); if(rc == 0) { errno = ETIMEDOUT; return NULL; } int err; socklen_t errsz = sizeof(err); rc = getsockopt(s, SOL_SOCKET, SO_ERROR, (void*)&err, &errsz); if(rc != 0) { err = errno; fdclean(s); close(s); errno = err; return NULL; } if(err != 0) { fdclean(s); close(s); errno = err; return NULL; } } /* Create the object. */ struct mill_tcpconn *conn = malloc(sizeof(struct mill_tcpconn)); if(!conn) { fdclean(s); close(s); errno = ENOMEM; return NULL; } tcpconn_init(conn, s); errno = 0; return (tcpsock)conn; }
void mfclose(mfile f) { fdclean(f->fd); int rc = close(f->fd); mill_assert(rc == 0); free(f); return; }
unixsock unixaccept(unixsock s, int64_t deadline) { if(s->type != MILL_UNIXLISTENER) mill_panic("trying to accept on a socket that isn't listening"); struct mill_unixlistener *l = (struct mill_unixlistener*)s; while(1) { /* Try to get new connection (non-blocking). */ int as = accept(l->fd, NULL, NULL); if (as >= 0) { mill_unixtune(as); struct mill_unixconn *conn = malloc(sizeof(struct mill_unixconn)); if(!conn) { fdclean(as); close(as); errno = ENOMEM; return NULL; } unixconn_init(conn, as); errno = 0; return (unixsock)conn; } mill_assert(as == -1); if(errno != EAGAIN && errno != EWOULDBLOCK) return NULL; /* Wait till new connection is available. */ int rc = fdwait(l->fd, FDW_IN, deadline); if(rc == 0) { errno = ETIMEDOUT; return NULL; } mill_assert(rc == FDW_IN); } }
unixsock unixlisten(const char *addr, int backlog) { struct sockaddr_un su; int rc = mill_unixresolve(addr, &su); if (rc != 0) { return NULL; } /* Open the listening socket. */ int s = socket(AF_UNIX, SOCK_STREAM, 0); if(s == -1) return NULL; mill_unixtune(s); /* Start listening. */ rc = bind(s, (struct sockaddr*)&su, sizeof(struct sockaddr_un)); if(rc != 0) return NULL; rc = listen(s, backlog); if(rc != 0) return NULL; /* Create the object. */ struct mill_unixlistener *l = malloc(sizeof(struct mill_unixlistener)); if(!l) { fdclean(s); close(s); errno = ENOMEM; return NULL; } l->sock.type = MILL_UNIXLISTENER; l->fd = s; errno = 0; return &l->sock; }
tcpsock tcplisten(ipaddr addr, int backlog) { /* Open the listening socket. */ int s = socket(mill_ipfamily(addr), SOCK_STREAM, 0); if(s == -1) return NULL; mill_tcptune(s); /* Start listening. */ int rc = bind(s, (struct sockaddr*)&addr, mill_iplen(addr)); if(rc != 0) return NULL; rc = listen(s, backlog); if(rc != 0) return NULL; /* If the user requested an ephemeral port, retrieve the port number assigned by the OS now. */ int port = mill_ipport(addr); if(!port == 0) { ipaddr baddr; socklen_t len = sizeof(ipaddr); rc = getsockname(s, (struct sockaddr*)&baddr, &len); if(rc == -1) { int err = errno; fdclean(s); close(s); errno = err; return NULL; } port = mill_ipport(baddr); } /* Create the object. */ struct mill_tcplistener *l = malloc(sizeof(struct mill_tcplistener)); if(!l) { fdclean(s); close(s); errno = ENOMEM; return NULL; } l->sock.type = MILL_TCPLISTENER; l->fd = s; l->port = port; errno = 0; return &l->sock; }
void tcpclose(tcpsock s) { if(s->type == MILL_TCPLISTENER) { struct mill_tcplistener *l = (struct mill_tcplistener*)s; fdclean(l->fd); int rc = close(l->fd); mill_assert(rc == 0); free(l); return; } if(s->type == MILL_TCPCONN) { struct mill_tcpconn *c = (struct mill_tcpconn*)s; fdclean(c->fd); int rc = close(c->fd); mill_assert(rc == 0); free(c); return; } mill_assert(0); }
void unixclose(unixsock s) { if(s->type == MILL_UNIXLISTENER) { struct mill_unixlistener *l = (struct mill_unixlistener*)s; fdclean(l->fd); int rc = close(l->fd); mill_assert(rc == 0); free(l); return; } if(s->type == MILL_UNIXCONN) { struct mill_unixconn *c = (struct mill_unixconn*)s; fdclean(c->fd); int rc = close(c->fd); mill_assert(rc == 0); free(c); return; } mill_assert(0); }
struct mill_udpsock *mill_udplisten_(ipaddr addr) { /* Open the listening socket. */ int s = socket(mill_ipfamily(addr), SOCK_DGRAM, 0); if(s == -1) return NULL; mill_udptune(s); /* Start listening. */ int rc = bind(s, (struct sockaddr*)&addr, mill_iplen(addr)); if(rc != 0) return NULL; /* If the user requested an ephemeral port, retrieve the port number assigned by the OS now. */ int port = mill_ipport(addr); if(port) { ipaddr baddr; socklen_t len = sizeof(ipaddr); rc = getsockname(s, (struct sockaddr*)&baddr, &len); if(rc == -1) { int err = errno; fdclean(s); close(s); errno = err; return NULL; } port = mill_ipport(baddr); } /* Create the object. */ struct mill_udpsock *us = malloc(sizeof(struct mill_udpsock)); if(!us) { fdclean(s); close(s); errno = ENOMEM; return NULL; } us->fd = s; us->port = port; errno = 0; return us; }
void unixpair(unixsock *a, unixsock *b) { if(!a || !b) { errno = EINVAL; return; } int fd[2]; int rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); if (rc != 0) return; mill_unixtune(fd[0]); mill_unixtune(fd[1]); struct mill_unixconn *conn = malloc(sizeof(struct mill_unixconn)); if(!conn) { fdclean(fd[0]); close(fd[0]); fdclean(fd[1]); close(fd[1]); errno = ENOMEM; return; } unixconn_init(conn, fd[0]); *a = (unixsock)conn; conn = malloc(sizeof(struct mill_unixconn)); if(!conn) { free(*a); fdclean(fd[0]); close(fd[0]); fdclean(fd[1]); close(fd[1]); errno = ENOMEM; return; } unixconn_init(conn, fd[1]); *b = (unixsock)conn; errno = 0; }
unixsock unixconnect(const char *addr) { struct sockaddr_un su; int rc = mill_unixresolve(addr, &su); if (rc != 0) { return NULL; } /* Open a socket. */ int s = socket(AF_UNIX, SOCK_STREAM, 0); if(s == -1) return NULL; mill_unixtune(s); /* Connect to the remote endpoint. */ rc = connect(s, (struct sockaddr*)&su, sizeof(struct sockaddr_un)); if(rc != 0) { int err = errno; mill_assert(rc == -1); fdclean(s); close(s); errno = err; return NULL; } /* Create the object. */ struct mill_unixconn *conn = malloc(sizeof(struct mill_unixconn)); if(!conn) { fdclean(s); close(s); errno = ENOMEM; return NULL; } unixconn_init(conn, s); errno = 0; return (unixsock)conn; }
int dsconnect(int s, const struct sockaddr *addr, socklen_t addrlen, int64_t deadline) { int err; /* Initiate connect. */ int rc = connect(s, addr, addrlen); if(rc == 0) return 0; if(dill_slow(errno != EINPROGRESS)) {err = errno; goto error;} /* Connect is in progress. Let's wait till it's done. */ rc = fdout(s, deadline); if(dill_slow(rc == -1)) {err = errno; goto error;} /* Retrieve the error from the socket, if any. */ socklen_t errsz = sizeof(err); rc = getsockopt(s, SOL_SOCKET, SO_ERROR, (void*)&err, &errsz); if(dill_slow(rc != 0)) {err = errno; goto error;} if(dill_slow(err != 0)) goto error; return 0; error: fdclean(s); close(s); errno = err; return -1; }
mfile mfopen(const char *pathname, int flags, mode_t mode) { /* Open the file. */ int fd = open(pathname, flags, mode); if (fd == -1) return NULL; mill_filetune(fd); /* Create the object. */ struct mill_file *f = malloc(sizeof(struct mill_file)); if(!f) { fdclean(fd); close(fd); errno = ENOMEM; return NULL; } f->fd = fd; f->ifirst = 0; f->ilen = 0; f->olen = 0; errno = 0; return f; }
tcpsock tcpaccept(tcpsock s, int64_t deadline) { if(s->type != MILL_TCPLISTENER) mill_panic("trying to accept on a socket that isn't listening"); struct mill_tcplistener *l = (struct mill_tcplistener*)s; socklen_t addrlen; ipaddr addr; while(1) { /* Try to get new connection (non-blocking). */ addrlen = sizeof(addr); int as = accept(l->fd, (struct sockaddr *)&addr, &addrlen); if (as >= 0) { mill_tcptune(as); struct mill_tcpconn *conn = malloc(sizeof(struct mill_tcpconn)); if(!conn) { fdclean(as); close(as); errno = ENOMEM; return NULL; } tcpconn_init(conn, as); conn->addr = addr; errno = 0; return (tcpsock)conn; } mill_assert(as == -1); if(errno != EAGAIN && errno != EWOULDBLOCK) return NULL; /* Wait till new connection is available. */ int rc = fdwait(l->fd, FDW_IN, deadline); if(rc == 0) { errno = ETIMEDOUT; return NULL; } mill_assert(rc == FDW_IN); } }
void mill_udpclose_(struct mill_udpsock *s) { fdclean(s->fd); int rc = close(s->fd); mill_assert(rc == 0); free(s); }
int dsclose(int s) { fdclean(s); return close(s); }
ipaddr mill_ipremote_(const char *name, int port, int mode, int64_t deadline) { int rc; ipaddr addr = mill_ipliteral(name, port, mode); if(errno == 0) return addr; /* Load DNS config files, unless they are already chached. */ if(mill_slow(!mill_dns_conf)) { /* TODO: Maybe re-read the configuration once in a while? */ mill_dns_conf = dns_resconf_local(&rc); mill_assert(mill_dns_conf); mill_dns_hosts = dns_hosts_local(&rc); mill_assert(mill_dns_hosts); mill_dns_hints = dns_hints_local(mill_dns_conf, &rc); mill_assert(mill_dns_hints); } /* Let's do asynchronous DNS query here. */ struct dns_resolver *resolver = dns_res_open(mill_dns_conf, mill_dns_hosts, mill_dns_hints, NULL, dns_opts(), &rc); mill_assert(resolver); mill_assert(port >= 0 && port <= 0xffff); char portstr[8]; snprintf(portstr, sizeof(portstr), "%d", port); struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; struct dns_addrinfo *ai = dns_ai_open(name, portstr, DNS_T_A, &hints, resolver, &rc); mill_assert(ai); dns_res_close(resolver); struct addrinfo *ipv4 = NULL; struct addrinfo *ipv6 = NULL; struct addrinfo *it = NULL; while(1) { rc = dns_ai_nextent(&it, ai); if(rc == EAGAIN) { int fd = dns_ai_pollfd(ai); mill_assert(fd >= 0); int events = fdwait(fd, FDW_IN, deadline); /* There's no guarantee that the file descriptor will be reused in next iteration. We have to clean the fdwait cache here to be on the safe side. */ fdclean(fd); if(mill_slow(!events)) { errno = ETIMEDOUT; return addr; } mill_assert(events == FDW_IN); continue; } if(rc == ENOENT) break; if(!ipv4 && it && it->ai_family == AF_INET) { ipv4 = it; } else if(!ipv6 && it && it->ai_family == AF_INET6) { ipv6 = it; } else { free(it); } if(ipv4 && ipv6) break; } switch(mode) { case IPADDR_IPV4: if(ipv6) { free(ipv6); ipv6 = NULL; } break; case IPADDR_IPV6: if(ipv4) { free(ipv4); ipv4 = NULL; } break; case 0: case IPADDR_PREF_IPV4: if(ipv4 && ipv6) { free(ipv6); ipv6 = NULL; } break; case IPADDR_PREF_IPV6: if(ipv6 && ipv4) { free(ipv4); ipv4 = NULL; } break; default: mill_assert(0); } if(ipv4) { struct sockaddr_in *inaddr = (struct sockaddr_in*)&addr; memcpy(inaddr, ipv4->ai_addr, sizeof (struct sockaddr_in)); inaddr->sin_port = htons(port); dns_ai_close(ai); free(ipv4); errno = 0; return addr; } if(ipv6) { struct sockaddr_in6 *inaddr = (struct sockaddr_in6*)&addr; memcpy(inaddr, ipv6->ai_addr, sizeof (struct sockaddr_in6)); inaddr->sin6_port = htons(port); dns_ai_close(ai); free(ipv6); errno = 0; return addr; } dns_ai_close(ai); ((struct sockaddr*)&addr)->sa_family = AF_UNSPEC; errno = EADDRNOTAVAIL; return addr; }
int main() { int rc; /* Check invalid fd. */ rc = fdin(33, -1); assert(rc == -1 && errno == EBADF); rc = fdout(33, -1); assert(rc == -1 && errno == EBADF); /* Create a pair of file deshndliptors for testing. */ int fds[2]; rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); errno_assert(rc == 0); /* Check for in & out. */ rc = fdout(fds[0], 0); errno_assert(rc == 0); rc = fdin(fds[0], 0); assert(rc == -1 && errno == ETIMEDOUT); /* Check with infinite timeout. */ rc = fdout(fds[0], -1); errno_assert(rc == 0); /* Check with the timeout that doesn't expire. */ rc = fdout(fds[0], now() + 100); errno_assert(rc == 0); /* Check with the timeout that does expire. */ int64_t deadline = now() + 100; rc = fdin(fds[0], deadline); assert(rc == -1 && errno == ETIMEDOUT); int64_t diff = now() - deadline; time_assert(diff, 0); /* Check cancelation. */ int hndl1 = go(cancel(fds[0])); errno_assert(hndl1 >= 0); rc = hclose(hndl1); errno_assert(rc == 0); /* Check for in. */ ssize_t sz = send(fds[1], "A", 1, 0); errno_assert(sz == 1); rc = fdin(fds[0], -1); errno_assert(rc == 0); char c; sz = recv(fds[0], &c, 1, 0); errno_assert(sz == 1); /* Two interleaved deadlines. */ int64_t start = now(); int hndl2 = go(trigger(fds[0], start + 50)); errno_assert(hndl2 >= 0); rc = fdin(fds[1], start + 90); errno_assert(rc == 0); diff = now() - start; time_assert(diff, 50); rc = hclose(hndl2); errno_assert(rc == 0); /* Check whether closing the connection is reported. */ ssize_t nbytes = send(fds[1], "ABC", 3, 0); errno_assert(nbytes == 3); rc = fdclean(fds[1]); errno_assert(rc == 0); rc = close(fds[1]); errno_assert(rc == 0); rc = msleep(now() + 50); errno_assert(rc == 0); rc = fdin(fds[0], -1); errno_assert(rc == 0); rc = fdout(fds[0], -1); errno_assert(rc == 0); char buf[10]; nbytes = recv(fds[0], buf, sizeof(buf), 0); assert(nbytes == 3); rc = fdin(fds[0], -1); errno_assert(rc == 0); nbytes = recv(fds[0], buf, sizeof(buf), 0); assert(nbytes == 0 || (nbytes == -1 && errno == ECONNRESET)); rc = fdclean(fds[0]); errno_assert(rc == 0); rc = close(fds[0]); errno_assert(rc == 0); /* Test whether fd is removed from the pollset after fdin/fdout times out. */ int pp[2]; rc = pipe(pp); assert(rc == 0); rc = fdin(pp[0], now() + 10); assert(rc == -1 && errno == ETIMEDOUT); nbytes = write(pp[1], "ABC", 3); assert(nbytes == 3); rc = msleep(now() + 100); assert(rc == 0); rc = fdclean(pp[0]); errno_assert(rc == 0); rc = fdclean(pp[1]); errno_assert(rc == 0); rc = close(pp[0]); assert(rc == 0); rc = close(pp[1]); assert(rc == 0); return 0; }