blob: 9fa8a9e0d3ad9cc15c20be0cf7093db74e5d63f1 [file] [log] [blame]
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <errno.h>
#undef sun
#define sun sockun
extern int _p9netfd(char*);
static char *unknown = "unknown";
static int
convert(int s, struct sockaddr *sa, char **lsys, char **lserv, char **laddr)
{
struct sockaddr_un *sun;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
uchar *ip;
u32int ipl;
socklen_t sn;
int n;
char *net;
switch(sa->sa_family){
case AF_INET:
sin = (void*)sa;
ip = (uchar*)&sin->sin_addr;
ipl = *(u32int*)ip;
if(ipl == 0)
*lsys = strdup("*");
else
*lsys = smprint("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
*lserv = smprint("%d", ntohs(sin->sin_port));
sn = sizeof n;
if(getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) < 0)
return -1;
if(n == SOCK_STREAM)
net = "tcp";
else if(n == SOCK_DGRAM)
net = "udp";
else{
werrstr("unknown network type");
return -1;
}
*laddr = smprint("%s!%s!%s", net, *lsys, *lserv);
if(*lsys == nil || *lserv == nil || *laddr == nil)
return -1;
return 0;
case AF_INET6:
sin6 = (void*)sa;
if (memcmp(&sin6->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0)
*lsys = strdup("*");
else{
*lsys = malloc(INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &sin6->sin6_addr, *lsys, INET6_ADDRSTRLEN);
}
*lserv = smprint("%d", ntohs(sin6->sin6_port));
sn = sizeof n;
if(getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) < 0)
return -1;
if(n == SOCK_STREAM)
net = "tcp";
else if(n == SOCK_DGRAM)
net = "udp";
else{
werrstr("unknown network type");
return -1;
}
*laddr = smprint("%s!%s!%s", net, *lsys, *lserv);
if(*lsys == nil || *lserv == nil || *laddr == nil)
return -1;
return 0;
case AF_UNIX:
sun = (void*)sa;
*lsys = unknown;
*lserv = unknown;
*laddr = smprint("unix!%s", sun->sun_path);
if(*laddr == nil)
return -1;
return 0;
default:
werrstr("unknown socket family");
return -1;
}
}
NetConnInfo*
getnetconninfo(char *dir, int fd)
{
socklen_t sn;
union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
struct sockaddr_un sun;
} u;
NetConnInfo *nci;
if(dir){
if((fd = _p9netfd(dir)) < 0){
werrstr("no such network connection %s", dir);
return nil;
}
}
nci = mallocz(sizeof *nci, 1);
if(nci == nil)
goto err;
nci->dir = smprint("/dev/fd/%d", fd);
nci->root = strdup("/net");
nci->spec = unknown;
if(nci->dir == nil || nci->root == nil)
goto err;
sn = sizeof u;
if(getsockname(fd, &u.sa, &sn) < 0)
goto err;
if(convert(fd, &u.sa, &nci->lsys, &nci->lserv, &nci->laddr) < 0)
goto err;
sn = sizeof u;
if(getpeername(fd, &u.sa, &sn) < 0)
goto err;
if(convert(fd, &u.sa, &nci->rsys, &nci->rserv, &nci->raddr) < 0)
goto err;
return nci;
err:
freenetconninfo(nci);
return nil;
}
static void
xfree(void *v)
{
if(v != nil && v != unknown)
free(v);
}
void
freenetconninfo(NetConnInfo *nci)
{
if(nci == nil)
return;
xfree(nci->dir);
xfree(nci->root);
xfree(nci->spec);
xfree(nci->lsys);
xfree(nci->lserv);
xfree(nci->rsys);
xfree(nci->rserv);
xfree(nci->laddr);
xfree(nci->raddr);
free(nci);
}