* and net.visibleipv4 are unspecified the address of the hub
* connection is used. */
{CONF_VAR_STRING, "publicif", {.str = L""}},
- /* Diffserv should be supported on IPv4, too, but I don't know the
- * API to do that. */
/** The Diffserv value to use on IPv6 connections when the
* minimize cost TOS value is used (see the TOS VALUES
* section). */
* minimize delay TOS value is used (see the TOS VALUES
* section). */
{CONF_VAR_INT, "diffserv-mindelay", {.num = 0}},
+ /** If enabled, the IP TOS interface will be used to set Diffserv
+ * codepoints on IPv4 sockets, by shifting the DSCP value two bits
+ * to the left (remember, the DSCP field in the IPv4 header is
+ * defined as the 6 uppermost bits of the TOS field, the lower two
+ * being left for ECN). This may only work on Linux. */
+ {CONF_VAR_BOOL, "dscp-tos", {.num = 0}},
{CONF_VAR_END}
};
} d;
};
+static int getlocalname(int fd, struct sockaddr **namebuf, socklen_t *lenbuf);
+
static struct ufd *ufds = NULL;
static struct scons *rbatch, *wbatch, *cbatch;
int numsocks = 0;
new->refcount = 1;
new->state = -1;
new->dgram = dgram;
+ new->maxbuf = 65536;
numsocks++;
return(new);
}
sk->back->state = state;
}
+struct socket *netsockpipe(void)
+{
+ struct socket *sk;
+
+ sk = sockpair(0);
+ sksetstate(sk, SOCK_EST);
+ return(sk);
+}
+
static void closeufd(struct ufd *ufd)
{
if(ufd->fd != -1)
if(ufd == ufds)
ufds = ufd->next;
closeufd(ufd);
- putsock(ufd->sk);
+ if(ufd->sk != NULL)
+ putsock(ufd->sk);
if(ufd->type == UFD_SOCK) {
if(ufd->d.s.remote != NULL)
free(ufd->d.s.remote);
ufd = memset(smalloc(sizeof(*ufd)), 0, sizeof(*ufd));
ufd->fd = fd;
+ ufd->type = type;
if(sk != NULL) {
getsock(ufd->sk = sk);
sk->ufd = ufd;
freeufd(nufd);
return(NULL);
}
+ sksetstate(nsk, SOCK_EST);
if(ufd->type == UFD_SOCK) {
nufd->d.s.family = ufd->d.s.family;
nufd->d.s.type = ufd->d.s.type;
sk->refcount++;
}
+static void sockdebug(int level, struct socket *sk, char *format, ...)
+{
+ va_list args;
+ char *tb;
+
+ if((sk->dbgnm == NULL) || (level > sk->dbglvl))
+ return;
+ va_start(args, format);
+ tb = vsprintf2(format, args);
+ va_end(args);
+ fprintf(stderr, "%s: %s\n", sk->dbgnm, tb);
+ free(tb);
+}
+
+void socksetdebug(struct socket *sk, int level, char *nm, ...)
+{
+ va_list args;
+ char *tb;
+
+ va_start(args, nm);
+ tb = vsprintf2(nm, args);
+ va_end(args);
+ sk->dbgnm = sprintf2("%s (f)", tb);
+ sk->back->dbgnm = sprintf2("%s (b)", tb);
+ free(tb);
+ sk->dbglvl = level;
+ sk->back->dbglvl = level;
+ sockdebug(1, sk, "enabled debugging");
+}
+
static void freesock(struct socket *sk)
{
struct dgrambuf *buf;
+ sockdebug(1, sk, "freeing socket");
if(sk->dgram) {
while((buf = sk->buf.d.f) != NULL) {
sk->buf.d.f = buf->next;
if(sk->buf.s.buf != NULL)
free(sk->buf.s.buf);
}
+ if(sk->dbgnm != NULL)
+ free(sk->dbgnm);
free(sk);
numsocks--;
}
void putsock(struct socket *sk)
{
+ struct socket *back;
+
if(--(sk->refcount) < 0) {
flog(LOG_CRIT, "BUG: socket refcount < 0");
abort();
}
if((sk->refcount == 0) && (sk->back->refcount == 0)) {
+ back = sk->back;
freesock(sk);
- freesock(sk->back);
+ freesock(back);
}
}
+void quitsock(struct socket *sk)
+{
+ sk->readcb = NULL;
+ sk->writecb = NULL;
+ sk->errcb = NULL;
+ putsock(sk);
+}
+
static void linksock(struct scons **list, struct socket *sk)
{
struct scons *sc;
/* Read as the preterite of `read' */
void sockread(struct socket *sk)
{
+ if((sockgetdatalen(sk) == 0) && (sk->eos == 1))
+ linksock(&rbatch, sk);
linksock(&wbatch, sk->back);
}
if((sk->buf.s.buf == NULL) || (sk->buf.s.datasize == 0))
{
*size = 0;
+ sockdebug(2, sk, "read 0 bytes", *size);
return(NULL);
}
buf = sk->buf.s.buf;
sk->buf.s.bufsize = sk->buf.s.datasize = 0;
sockread(sk);
}
+ sockdebug(2, sk, "read %zi bytes", *size);
return(buf);
}
struct sockaddr *remote;
socklen_t remotelen;
+ sockdebug(2, sk, "queued %zi bytes", size);
if(size == 0)
return;
if(sk->state == SOCK_STL)
linksock(&rbatch, sk->back);
}
-void sockeos(struct socket *sk)
-{
- sksetstate(sk, SOCK_STL);
- if(sk->back->eos == 0)
- sk->back->eos = 1;
- linksock(&rbatch, sk->back);
-}
-
-static void sockerror(struct socket *sk, int en)
+void sockerror(struct socket *sk, int en)
{
sksetstate(sk, SOCK_STL);
if(sk->back->errcb != NULL)
freedgbuf(dbuf);
if((ufd->type != UFD_SOCK) || !((ufd->d.s.family == AF_INET) || (ufd->d.s.family == AF_INET6)))
{
- sockeos(ufd->sk);
+ closesock(ufd->sk);
closeufd(ufd);
}
return;
{
free(buf);
closeufd(ufd);
- sockeos(ufd->sk);
+ closesock(ufd->sk);
return;
}
sockqueue(ufd->sk, buf, ret);
}
}
-static void sockflush(struct ufd *ufd)
+static int sockflush(struct ufd *ufd)
{
int ret;
struct dgrambuf *dbuf;
int dgram;
- if((dgram = ufddgram(ufd)) < 0)
- return;
+ if((dgram = ufddgram(ufd)) < 0) {
+ errno = EBADFD;
+ return(-1);
+ }
if(dgram) {
dbuf = sockgetdgbuf(ufd->sk);
sendto(ufd->fd, dbuf->data, dbuf->size, MSG_DONTWAIT | MSG_NOSIGNAL, dbuf->addr, dbuf->addrlen);
ret = send(ufd->fd, ufd->sk->buf.s.buf, ufd->sk->buf.s.datasize, MSG_DONTWAIT | MSG_NOSIGNAL);
else
ret = write(ufd->fd, ufd->sk->buf.s.buf, ufd->sk->buf.s.datasize);
- if(ret < 0) {
- /* For now, assume transient error, since
- * the socket is polled for errors */
- return;
- }
+ if(ret < 0)
+ return(-1);
if(ret > 0) {
memmove(ufd->sk->buf.s.buf, ((char *)ufd->sk->buf.s.buf) + ret, ufd->sk->buf.s.datasize -= ret);
sockread(ufd->sk);
}
}
+ return(0);
}
void closesock(struct socket *sk)
{
-/*
- struct sockaddr_un *un;
-
- if((sk->family == AF_UNIX) && !sockgetlocalname(sk, (struct sockaddr **)(void *)&un, NULL) && (un->sun_family == PF_UNIX))
- {
- if((sk->state == SOCK_LST) && strchr(un->sun_path, '/'))
- {
- if(unlink(un->sun_path))
- flog(LOG_WARNING, "could not unlink Unix socket %s: %s", un->sun_path, strerror(errno));
- }
- }
-*/
+ sockdebug(1, sk, "closed");
sksetstate(sk, SOCK_STL);
- sockeos(sk);
+ if(sk->back->eos == 0)
+ sk->back->eos = 1;
+ linksock(&rbatch, sk->back);
}
size_t sockgetdatalen(struct socket *sk)
return(ret);
}
-size_t sockqueuesize(struct socket *sk)
+/* size_t sockqueuesize(struct socket *sk) */
+/* { */
+/* return(sockgetdatalen(sk->back)); */
+/* } */
+
+size_t socktqueuesize(struct socket *sk)
{
- return(sockgetdatalen(sk->back));
+ size_t ret;
+
+ ret = 0;
+ while(1) {
+ ret += sockgetdatalen(sk->back);
+ if((sk = sk->back->pnext) == NULL)
+ return(ret);
+ }
+}
+
+ssize_t sockqueueleft(struct socket *sk)
+{
+ return(sk->back->maxbuf - sockgetdatalen(sk->back));
}
/*
return(0);
}
+void closelport(struct lport *lp)
+{
+ struct ufd *ufd;
+ struct sockaddr_un *un;
+
+ ufd = lp->ufd;
+ if((ufd->d.l.family == AF_UNIX) && !getlocalname(ufd->fd, (struct sockaddr **)(void *)&un, NULL) && (un->sun_family == PF_UNIX) && strchr(un->sun_path, '/')) {
+ if(unlink(un->sun_path))
+ flog(LOG_WARNING, "could not unlink Unix socket %s: %s", un->sun_path, strerror(errno));
+ }
+ freeufd(lp->ufd);
+}
+
/*
* The difference between netcslisten() and netcslistenlocal() is that
* netcslistenlocal() always listens on the local host, instead of
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &intbuf, sizeof(intbuf));
}
ufd = mkufd(fd, UFD_LISTEN, NULL);
- ufd->d.l.lp = lp;
ufd->d.l.family = name->sa_family;
+ lp = memset(smalloc(sizeof(*lp)), 0, sizeof(*lp));
+ lp->ufd = ufd;
+ ufd->d.l.lp = lp;
if((bind(fd, name, namelen) < 0) && ((errno != EADDRINUSE) || (rebindunix(ufd, name, namelen) < 0))) {
freeufd(ufd);
return(NULL);
struct ufd *nufd;
nufd = dupufd(sk->back->ufd);
- sk = nufd->sk->back;
+ getsock(sk = nufd->sk->back);
memcpy(nufd->d.s.remote = smalloc(addrlen), addr, nufd->d.s.remotelen = addrlen);
nufd->ignread = 1;
return(sk);
memcpy(sk->ufd->d.s.remote = smalloc(addrlen), addr, sk->ufd->d.s.remotelen = addrlen);
sk->back->conncb = func;
sk->back->data = data;
+ getsock(sk->back);
+ putsock(sk);
if(!connect(sk->ufd->fd, addr, addrlen))
{
sksetstate(sk, SOCK_EST);
sksetstate(sk, SOCK_SYN);
return(sk->back);
}
- putsock(sk);
+ putsock(sk->back);
return(NULL);
}
errno = EOPNOTSUPP;
#endif
}
+static void runbatches(void)
+{
+ struct scons *sc, *nsc;
+
+ for(sc = cbatch, cbatch = NULL; sc; sc = nsc) {
+ nsc = sc->n;
+ if(sc->s->conncb != NULL)
+ sc->s->conncb(sc->s, 0, sc->s->data);
+ putsock(sc->s);
+ free(sc);
+ }
+ for(sc = rbatch, rbatch = NULL; sc; sc = nsc) {
+ nsc = sc->n;
+ if(sc->s->readcb != NULL)
+ sc->s->readcb(sc->s, sc->s->data);
+ if((sockgetdatalen(sc->s) == 0) && (sc->s->eos == 1)) {
+ if(sc->s->errcb != NULL)
+ sc->s->errcb(sc->s, 0, sc->s->data);
+ sc->s->eos = 2;
+ }
+ putsock(sc->s);
+ free(sc);
+ }
+ for(sc = wbatch, wbatch = NULL; sc; sc = nsc) {
+ nsc = sc->n;
+ if(sc->s->writecb != NULL)
+ sc->s->writecb(sc->s, sc->s->data);
+ putsock(sc->s);
+ free(sc);
+ }
+}
+
+static void cleansocks(void)
+{
+ struct ufd *ufd, *next;
+ int dead;
+
+ for(ufd = ufds; ufd != NULL; ufd = next) {
+ next = ufd->next;
+ if(ufd->sk) {
+ dead = (ufd->fd < 0);
+ if(ufd->sk->state == SOCK_STL)
+ dead = 1;
+ if((ufd->sk->state == SOCK_EST) && (sockgetdatalen(ufd->sk) == 0))
+ dead = 1;
+ if(!dead)
+ continue;
+ if(ufd->sk->eos == 1) {
+ ufd->sk->eos = 2;
+ closeufd(ufd);
+ closesock(ufd->sk);
+ }
+ if((ufd->sk->refcount == 1) && (ufd->sk->back->refcount == 0)) {
+ freeufd(ufd);
+ continue;
+ }
+ }
+ }
+}
+
int pollsocks(int timeout)
{
int ret;
socklen_t retlen;
int newfd, maxfd;
fd_set rfds, wfds, efds;
- struct ufd *ufd, *nufd, *next;
+ struct ufd *ufd, *nufd;
struct socket *nsk;
struct sockaddr_storage ss;
socklen_t sslen;
struct timeval tv;
- struct scons *sc, *nsc;
+ cleansocks();
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&efds);
for(maxfd = 0, ufd = ufds; ufd != NULL; ufd = ufd->next) {
if(ufd->fd < 0)
continue;
- if(!ufd->ignread)
+ if(!ufd->ignread && ((ufd->sk == NULL) || (sockqueueleft(ufd->sk) > 0)))
FD_SET(ufd->fd, &rfds);
if(ufd->sk != NULL) {
if(sockgetdatalen(ufd->sk) > 0)
if(ufd->fd > maxfd)
maxfd = ufd->fd;
}
+ if(rbatch || wbatch || cbatch)
+ timeout = 0;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
ret = select(maxfd + 1, &rfds, &wfds, &efds, (timeout < 0)?NULL:&tv);
}
return(1);
}
- for(ufd = ufds; ufd != NULL; ufd = ufd->next)
- {
+ for(ufd = ufds; ufd != NULL; ufd = ufd->next) {
if(ufd->sk < 0)
continue;
if(ufd->type == UFD_LISTEN) {
if(ss.ss_family == PF_UNIX)
acceptunix(nufd);
if(ufd->d.l.lp->acceptcb != NULL)
- ufd->d.l.lp->acceptcb(ufd->d.l.lp, nsk, ufd->d.l.lp->data);
+ ufd->d.l.lp->acceptcb(ufd->d.l.lp, nsk->back, ufd->d.l.lp->data);
putsock(nsk);
}
if(FD_ISSET(ufd->fd, &efds)) {
}
if(FD_ISSET(ufd->fd, &rfds))
sockrecv(ufd);
- if(FD_ISSET(ufd->fd, &wfds))
- sockflush(ufd);
- }
- }
- }
- for(ufd = ufds; ufd != NULL; ufd = next)
- {
- next = ufd->next;
- if(sockgetdatalen(ufd->sk) == 0) {
- if(ufd->sk->eos) {
- closeufd(ufd);
- closesock(ufd->sk);
- }
- if((ufd->sk->refcount == 1) && (ufd->sk->back->refcount == 0)) {
- freeufd(ufd);
- continue;
+ if(ufd->fd == -1)
+ continue;
+ if(FD_ISSET(ufd->fd, &wfds)) {
+ if(sockflush(ufd)) {
+ sockerror(ufd->sk, errno);
+ closeufd(ufd);
+ continue;
+ }
+ }
}
}
}
+ runbatches();
+ cleansocks();
return(1);
}
+static struct ufd *getskufd(struct socket *sk)
+{
+ while(1) {
+ if(sk->back->ufd != NULL)
+ return(sk->back->ufd);
+ if((sk = sk->back->pnext) == NULL)
+ break;
+ }
+ return(NULL);
+}
+
int socksettos(struct socket *sk, int tos)
{
int buf;
+ struct ufd *ufd;
+ int dscp2tos;
- if(sk->family == AF_UNIX)
+ ufd = getskufd(sk);
+ if(ufd->type != UFD_SOCK) {
+ errno = EOPNOTSUPP;
+ return(-1);
+ }
+ if(ufd->d.s.family == AF_UNIX)
return(0); /* Unix sockets are always perfect. :) */
- if(sk->family == AF_INET)
+ if(ufd->d.s.family == AF_INET)
{
+ dscp2tos = confgetint("net", "dscp-tos");
switch(tos)
{
case 0:
buf = 0;
break;
case SOCK_TOS_MINCOST:
- buf = 0x02;
+ if(dscp2tos)
+ buf = confgetint("net", "diffserv-mincost") << 2;
+ else
+ buf = 0x02;
break;
case SOCK_TOS_MAXREL:
- buf = 0x04;
+ if(dscp2tos)
+ buf = confgetint("net", "diffserv-maxrel") << 2;
+ else
+ buf = 0x04;
break;
case SOCK_TOS_MAXTP:
- buf = 0x08;
+ if(dscp2tos)
+ buf = confgetint("net", "diffserv-maxtp") << 2;
+ else
+ buf = 0x08;
break;
case SOCK_TOS_MINDELAY:
- buf = 0x10;
+ if(dscp2tos)
+ buf = confgetint("net", "diffserv-mindelay") << 2;
+ else
+ buf = 0x10;
break;
default:
flog(LOG_WARNING, "attempted to set unknown TOS value %i to IPv4 sock", tos);
return(-1);
}
- if(setsockopt(sk->fd, IPPROTO_IP, IP_TOS, &buf, sizeof(buf)) < 0)
+ if(setsockopt(ufd->fd, IPPROTO_IP, IP_TOS, &buf, sizeof(buf)) < 0)
{
flog(LOG_WARNING, "could not set sock TOS to %i: %s", tos, strerror(errno));
return(-1);
}
return(0);
}
- if(sk->family == AF_INET6)
+ if(ufd->d.s.family == AF_INET6)
{
switch(tos)
{
*/
return(0);
}
- flog(LOG_WARNING, "could not set TOS on sock of family %i", sk->family);
+ flog(LOG_WARNING, "could not set TOS on sock of family %i", ufd->d.s.family);
return(1);
}
return(0);
}
-int sockgetlocalname(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf)
+static int getlocalname(int fd, struct sockaddr **namebuf, socklen_t *lenbuf)
{
socklen_t len;
struct sockaddr_storage name;
*namebuf = NULL;
- if((sk->state == SOCK_STL) || (sk->fd < 0))
+ if(fd < 0)
return(-1);
len = sizeof(name);
- if(getsockname(sk->fd, (struct sockaddr *)&name, &len) < 0)
+ if(getsockname(fd, (struct sockaddr *)&name, &len) < 0)
{
flog(LOG_ERR, "BUG: alive socket with dead fd in sockgetlocalname (%s)", strerror(errno));
return(-1);
return(0);
}
+int lstgetlocalname(struct lport *lp, struct sockaddr **namebuf, socklen_t *lenbuf)
+{
+ struct ufd *ufd;
+
+ ufd = lp->ufd;
+ return(getlocalname(ufd->fd, namebuf, lenbuf));
+}
+
+int sockgetlocalname(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf)
+{
+ struct ufd *ufd;
+
+ ufd = getskufd(sk);
+ if(ufd->type != UFD_SOCK) {
+ errno = EOPNOTSUPP;
+ return(-1);
+ }
+ return(getlocalname(ufd->fd, namebuf, lenbuf));
+}
+
static void sethostaddr(struct sockaddr *dst, struct sockaddr *src)
{
if(dst->sa_family != src->sa_family)
return(0);
}
-int sockgetremotename(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf)
+static int getremotename(int fd, struct sockaddr **namebuf, socklen_t *lenbuf)
{
socklen_t len;
struct sockaddr *name;
-
- switch(confgetint("net", "mode"))
- {
+
+ switch(confgetint("net", "mode")) {
case 0:
*namebuf = NULL;
- if((sk->state == SOCK_STL) || (sk->fd < 0))
- {
- errno = EBADF;
- return(-1);
- }
- if(!sockgetlocalname(sk, &name, &len))
- {
+ if(!getlocalname(fd, &name, &len)) {
*namebuf = name;
*lenbuf = len;
makepublic(name);
}
}
+int sockgetremotename(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf)
+{
+ struct ufd *ufd;
+
+ ufd = getskufd(sk);
+ if(ufd->type != UFD_SOCK) {
+ errno = EOPNOTSUPP;
+ return(-1);
+ }
+ if(ufd->fd < 0) {
+ errno = EBADF;
+ return(-1);
+ }
+ return(getremotename(ufd->fd, namebuf, lenbuf));
+}
+
+int lstgetremotename(struct lport *lp, struct sockaddr **namebuf, socklen_t *lenbuf)
+{
+ struct ufd *ufd;
+
+ ufd = lp->ufd;
+ return(getremotename(ufd->fd, namebuf, lenbuf));
+}
+
int sockgetremotename2(struct socket *sk, struct socket *sk2, struct sockaddr **namebuf, socklen_t *lenbuf)
{
struct sockaddr *name1, *name2;
socklen_t len1, len2;
+ struct ufd *ufd1, *ufd2;
+
+ ufd1 = getskufd(sk);
+ ufd2 = getskufd(sk2);
+ if((ufd1->type != UFD_SOCK) || (ufd2->type != UFD_SOCK)) {
+ errno = EOPNOTSUPP;
+ return(-1);
+ }
+ if(ufd1->d.s.family != ufd2->d.s.family)
+ {
+ flog(LOG_ERR, "using sockgetremotename2 with sockets of differing family: %i %i", ufd1->d.s.family, ufd2->d.s.family);
+ return(-1);
+ }
+ if(getremotename(ufd1->fd, &name1, &len1))
+ return(-1);
+ if(getremotename(ufd2->fd, &name2, &len2)) {
+ free(name1);
+ return(-1);
+ }
+ sethostaddr(name1, name2);
+ free(name2);
+ *namebuf = name1;
+ *lenbuf = len1;
+ return(0);
+}
+
+int lstgetremotename2(struct lport *lp, struct socket *sk2, struct sockaddr **namebuf, socklen_t *lenbuf)
+{
+ struct sockaddr *name1, *name2;
+ socklen_t len1, len2;
+ struct ufd *ufd1, *ufd2;
- if(sk->family != sk2->family)
+ ufd1 = lp->ufd;
+ ufd2 = getskufd(sk2);
+ if(ufd2->type != UFD_SOCK) {
+ errno = EOPNOTSUPP;
+ return(-1);
+ }
+ if(ufd1->d.l.family != ufd2->d.s.family)
{
- flog(LOG_ERR, "using sockgetremotename2 with sockets of differing family: %i %i", sk->family, sk2->family);
+ flog(LOG_ERR, "using lstgetremotename2 with sockets of differing family: %i %i", ufd1->d.l.family, ufd2->d.s.family);
return(-1);
}
- if(sockgetremotename(sk, &name1, &len1))
+ if(getremotename(ufd1->fd, &name1, &len1))
return(-1);
- if(sockgetremotename(sk2, &name2, &len2)) {
+ if(getremotename(ufd2->fd, &name2, &len2)) {
free(name1);
return(-1);
}
return(0);
}
-void sockblock(struct socket *sk, int block)
+int getucred(struct socket *sk, uid_t *uid, gid_t *gid)
+{
+ struct ufd *ufd;
+
+ ufd = getskufd(sk);
+ if(ufd->type != UFD_SOCK) {
+ errno = EOPNOTSUPP;
+ return(-1);
+ }
+ if(ufd->d.s.family != AF_UNIX) {
+ errno = EOPNOTSUPP;
+ return(-1);
+ }
+ *uid = ufd->d.s.ucred.uid;
+ *gid = ufd->d.s.ucred.gid;
+ return(0);
+}
+
+/* void sockblock(struct socket *sk, int block) */
+/* { */
+/* struct ufd *ufd; */
+
+/* ufd = getskufd(sk); */
+/* ufd->ignread = block; */
+/* } */
+
+int sockfamily(struct socket *sk)
{
- sk->block = block;
+ struct ufd *ufd;
+
+ ufd = getskufd(sk);
+ if(ufd->type != UFD_SOCK) {
+ errno = EOPNOTSUPP;
+ return(-1);
+ }
+ return(ufd->d.s.family);
}
int sockpeeraddr(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf)
{
- return(-1);
+ struct ufd *ufd;
+
+ ufd = getskufd(sk);
+ if(ufd->type != UFD_SOCK) {
+ errno = EOPNOTSUPP;
+ return(-1);
+ }
+ if(ufd->d.s.remote == NULL)
+ return(-1);
+ *namebuf = memcpy(smalloc(ufd->d.s.remotelen), ufd->d.s.remote, ufd->d.s.remotelen);
+ if(lenbuf != NULL)
+ *lenbuf = ufd->d.s.remotelen;
+ return(0);
}
char *formatsockpeer(struct socket *sk)
static void terminate(void)
{
- while(sockets != NULL)
- unlinksock(sockets);
+ /*
+ while(ufds != NULL)
+ freeufd(ufds);
+ */
}
static struct module me =