| #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); |
| } |
| |