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 = mill_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; close(s); errno = err; return NULL; } if(err != 0) { close(s); errno = err; return NULL; } } /* Create the object. */ struct mill_tcpconn *conn = malloc(sizeof(struct mill_tcpconn)); if(!conn) { close(s); errno = ENOMEM; return NULL; } tcpconn_init(conn, s); errno = 0; return (tcpsock)conn; }
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; close(s); errno = err; return NULL; } port = mill_ipport(baddr); } /* Create the object. */ struct mill_tcplistener *l = malloc(sizeof(struct mill_tcplistener)); if(!l) { close(s); errno = ENOMEM; return NULL; } l->sock.type = MILL_TCPLISTENER; l->fd = s; l->port = port; errno = 0; return &l->sock; }
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); } }
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; while(1) { /* Try to get new connection (non-blocking). */ int as = accept(l->fd, NULL, NULL); if (as >= 0) { mill_tcptune(as); errno = 0; return &tcpconn_create(as)->sock; } 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); } }