156 lines
2.7 KiB
C
156 lines
2.7 KiB
C
#include <u.h>
|
|
#define NOPLAN9DEFINES
|
|
#include <libc.h>
|
|
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <sys/un.h>
|
|
#include <errno.h>
|
|
|
|
#undef sun
|
|
#define sun sockun
|
|
|
|
int
|
|
_p9netfd(char *dir)
|
|
{
|
|
int fd;
|
|
|
|
if(strncmp(dir, "/dev/fd/", 8) != 0)
|
|
return -1;
|
|
fd = strtol(dir+8, &dir, 0);
|
|
if(*dir != 0)
|
|
return -1;
|
|
return fd;
|
|
}
|
|
|
|
static void
|
|
putfd(char *dir, int fd)
|
|
{
|
|
snprint(dir, NETPATHLEN, "/dev/fd/%d", fd);
|
|
}
|
|
|
|
#undef unix
|
|
#define unix sockunix
|
|
|
|
static int
|
|
addrlen(struct sockaddr_storage *ss)
|
|
{
|
|
switch(ss->ss_family){
|
|
case AF_INET:
|
|
return sizeof(struct sockaddr_in);
|
|
case AF_INET6:
|
|
return sizeof(struct sockaddr_in6);
|
|
case AF_UNIX:
|
|
return sizeof(struct sockaddr_un);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
p9announce(char *addr, char *dir)
|
|
{
|
|
int proto;
|
|
char *buf, *unix;
|
|
char *net;
|
|
int port, s;
|
|
int n;
|
|
socklen_t sn;
|
|
struct sockaddr_storage ss;
|
|
|
|
buf = strdup(addr);
|
|
if(buf == nil)
|
|
return -1;
|
|
|
|
if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
if(strcmp(net, "tcp") == 0)
|
|
proto = SOCK_STREAM;
|
|
else if(strcmp(net, "udp") == 0)
|
|
proto = SOCK_DGRAM;
|
|
else if(strcmp(net, "unix") == 0)
|
|
goto Unix;
|
|
else{
|
|
werrstr("can only handle tcp, udp, and unix: not %s", net);
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
free(buf);
|
|
|
|
if((s = socket(ss.ss_family, proto, 0)) < 0)
|
|
return -1;
|
|
sn = sizeof n;
|
|
if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0
|
|
&& n == SOCK_STREAM){
|
|
n = 1;
|
|
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
|
|
}
|
|
if(bind(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0){
|
|
close(s);
|
|
return -1;
|
|
}
|
|
if(proto == SOCK_STREAM){
|
|
listen(s, 8);
|
|
putfd(dir, s);
|
|
}
|
|
return s;
|
|
|
|
Unix:
|
|
if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0)
|
|
return -1;
|
|
if(bind(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0){
|
|
if(errno == EADDRINUSE
|
|
&& connect(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0
|
|
&& errno == ECONNREFUSED){
|
|
/* dead socket, so remove it */
|
|
remove(unix);
|
|
close(s);
|
|
if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0)
|
|
return -1;
|
|
if(bind(s, (struct sockaddr*)&ss, addrlen(&ss)) >= 0)
|
|
goto Success;
|
|
}
|
|
close(s);
|
|
return -1;
|
|
}
|
|
Success:
|
|
listen(s, 8);
|
|
putfd(dir, s);
|
|
return s;
|
|
}
|
|
|
|
int
|
|
p9listen(char *dir, char *newdir)
|
|
{
|
|
int fd, one;
|
|
|
|
if((fd = _p9netfd(dir)) < 0){
|
|
werrstr("bad 'directory' in listen: %s", dir);
|
|
return -1;
|
|
}
|
|
|
|
if((fd = accept(fd, nil, nil)) < 0)
|
|
return -1;
|
|
|
|
one = 1;
|
|
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
|
|
|
|
putfd(newdir, fd);
|
|
return fd;
|
|
}
|
|
|
|
int
|
|
p9accept(int cfd, char *dir)
|
|
{
|
|
int fd;
|
|
|
|
if((fd = _p9netfd(dir)) < 0){
|
|
werrstr("bad 'directory' in accept");
|
|
return -1;
|
|
}
|
|
/* need to dup because the listen fd will be closed */
|
|
return dup(fd);
|
|
}
|