#include <u.h>
#include <libc.h>
#include <ureg.h>
#include "linuxsys.h"
#include "linux.h"
enum
{
AF_UNIX =1,
AF_INET =2,
AF_INET6 =10,
};
enum
{
SOCK_STREAM =1,
SOCK_DGRAM =2,
SOCK_RAW =3,
};
typedef struct Socket Socket;
struct Socket
{
int family;
int stype;
int protocol;
int fd;
int listenpid;
char net[40];
char name[80];
int naddr;
uchar addr[40];
};
static int
getsockaddr(Socket *sock, int remote, uchar *addr);
static void
destroysocktag(void *tag)
{
Socket *sock;
DPRINT("destroysocktag()...");
sock = *fdtagp(tag);
*fdtagp(tag) = nil;
if(sock->listenpid >= 0){
int fd;
char name[80];
snprint(name, sizeof(name), "/proc/%d/ctl", sock->listenpid);
if((fd = open(name, OWRITE)) >= 0){
fprint(fd, "kill");
close(fd);
}
sock->listenpid = -1;
}
free(sock);
}
static int
sys_socket(int family, int stype, int protocol)
{
char *net;
char buf[80];
int n;
int cfd, dfd;
Socket *sock;
void *tag;
DPRINT("socket(%d, %d, %d)...", family, stype, protocol);
net = nil;
switch(family){
case AF_INET:
switch(stype){
case SOCK_DGRAM:
net = "udp";
break;
case SOCK_STREAM:
net = "tcp";
break;
}
break;
}
if(net==nil)
return -1;
snprint(buf, sizeof(buf), "/net/%s/clone", net);
if((cfd = open(buf, ORDWR)) < 0)
return mkerror();
n = read(cfd, buf, sizeof(buf)-1);
if(n <= 0){
close(cfd);
return -1;
}
buf[n] = 0;
n = atoi(buf);
snprint(buf, sizeof(buf), "/net/%s/%d/data", net, n);
if((dfd = open(buf, ORDWR)) < 0){
close(cfd);
return -1;
}
close(cfd);
sock = malloc(sizeof(Socket));
if(sock == nil){
close(cfd);
return -ENOMEM;
}
memset(sock, 0, sizeof(Socket));
sock->family = family;
sock->stype = stype;
sock->protocol = protocol;
sock->listenpid = -1;
sock->fd = dfd;
sock->naddr = 0;
snprint(sock->net, sizeof(sock->net), "/net/%s", net);
snprint(sock->name, sizeof(sock->name), "%s/%d", sock->net, n);
DPRINT("created socket %s for fd %d...", sock->name, sock->fd);
tag = openfdtag(dfd, TAG_SOCK, 1);
assert(*fdtagp(tag) == nil);
*fdtagp(tag) = sock;
atdestroyfdtag(tag, destroysocktag);
closefdtag(tag);
return dfd;
}
static int
sys_connect(int fd, uchar *addr, int addrlen)
{
Socket *sock;
void *tag;
int n;
int cfd;
char buf[80];
DPRINT("connect(%d, 0x%p, %d)...", fd, addr, addrlen);
if((tag = openfdtag(fd, TAG_SOCK, 0))==nil)
return -ENOTSOCK;
sock = *fdtagp(tag);
switch(sock->family){
default:
closefdtag(tag);
return -1;
case AF_INET:
snprint(buf, sizeof(buf), "%s/ctl", sock->name);
if((cfd = open(buf, ORDWR)) < 0){
closefdtag(tag);
return mkerror();
}
sock->naddr = addrlen;
memcpy(sock->addr, addr, addrlen);
switch(sock->family){
case AF_INET:
snprint(buf, sizeof(buf), "connect %d.%d.%d.%d!%d",
(int)(addr[4]),
(int)(addr[5]),
(int)(addr[6]),
(int)(addr[7]),
(int)(((ulong)addr[2]<<8)|(ulong)addr[3]));
break;
}
n = strlen(buf);
if(write(cfd, buf, n) != n){
close(cfd);
closefdtag(tag);
return mkerror();
}
close(cfd);
buffd(sock->fd);
closefdtag(tag);
return 0;
}
}
static int
sys_bind(int fd, uchar *addr, int addrlen)
{
Socket *sock;
void *tag;
int n;
int cfd;
char buf[80];
DPRINT("bind(%d, 0x%p, %d)...", fd, addr, addrlen);
if((tag = openfdtag(fd, TAG_SOCK, 0))==nil)
return -ENOTSOCK;
sock = *fdtagp(tag);
switch(sock->family){
default:
closefdtag(tag);
fprint(2, "bind: unknown family %d\n", sock->family);
return -1;
case AF_INET:
snprint(buf, sizeof(buf), "%s/ctl", sock->name);
if((cfd = open(buf, ORDWR)) < 0){
fprint(2, "open failed: %r\n");
closefdtag(tag);
return mkerror();
}
sock->naddr = addrlen;
memcpy(sock->addr, addr, addrlen);
snprint(buf, sizeof(buf), "announce %d",
(int)(((ulong)addr[2]<<8)|(ulong)addr[3]));
n = strlen(buf);
if(write(cfd, buf, n) != n){
fprint(2, "announce failed: %r\n");
close(cfd);
closefdtag(tag);
return mkerror();
}
snprint(buf, sizeof(buf), "bind %d",
(int)(((ulong)addr[2]<<8)|(ulong)addr[3]));
n = strlen(buf);
if(write(cfd, buf, n) != n){
fprint(2, "bind failed: %r\n");
close(cfd);
closefdtag(tag);
return mkerror();
}
close(cfd);
closefdtag(tag);
return 0;
}
}
static void
listenproc(void *aux)
{
char *listen;
int pfd;
ulong *a;
a = aux;
listen = (char*)a[0];
pfd = (int)a[1];
for(;;){
int x;
int fd;
char buf[10];
int n;
fd = open(listen, ORDWR);
if(fd < 0)
break;
x = read(fd, buf, sizeof(buf)-1);
if(x <= 0){
close(fd);
break;
}
buf[x] = 0;
n = atoi(buf);
write(pfd, &n, sizeof(n));
x = read(pfd, buf, 1);
if(x < 1 || *buf!='1'){
close(fd);
break;
}
close(fd);
}
close(pfd);
}
static int
sys_listen(int fd)
{
Socket *sock;
void *tag;
int pfd[2];
char listen[80];
char name[80];
ulong a[2];
DPRINT("listen(%d)...\n", fd);
if((tag = openfdtag(fd, TAG_SOCK, 0))==nil)
return -ENOTSOCK;
sock = *fdtagp(tag);
strcpy(name, sock->name);
snprint(listen, sizeof(listen), "%s/listen", name);
pipe(pfd);
/* replace the data fd to a pipe listening on the listen-proc */
dup(fd, -1);
close(fd);
dup(pfd[0], fd);
close(pfd[0]);
a[0] = (ulong)listen;
a[1] = (ulong)pfd[1];
sock->listenpid = createxproc(listenproc, a, RFPROC, 8 * 1024);
buffd(sock->fd);
closefdtag(tag);
return 0;
}
static int
sys_accept(int fd, uchar *addr, int *paddrlen)
{
Socket *sock, *lsock;
void *tag;
char data[80];
int n;
DPRINT("accept(%d, 0x%p, 0x%p)...\n", fd, addr, paddrlen);
if((tag = openfdtag(fd, TAG_SOCK, 0))==nil)
return -ENOTSOCK;
lsock = *fdtagp(tag);
sock = malloc(sizeof(Socket));
if(sock == nil){
closefdtag(tag);
return -ENOMEM;
}
memset(sock, 0, sizeof(*sock));
sock->family = lsock->family;
sock->stype = lsock->stype;
sock->protocol = lsock->protocol;
sock->listenpid = -1;
sock->fd = -1;
sock->naddr = 0;
strcpy(sock->net, lsock->net);
closefdtag(tag);
if(_read(fd, &n, sizeof(n)) != sizeof(n)){
free(sock);
return -1;
}
snprint(sock->name, sizeof(sock->name), "%s/%d", sock->net, n);
snprint(data, sizeof(data), "%s/data", sock->name);
sock->fd = open(data, ORDWR);
sock->naddr = getsockaddr(sock, 0, sock->addr);
if(addr && paddrlen)
*paddrlen = getsockaddr(sock, 0, addr);
write(fd, "1", 1);
fd = sock->fd;
tag = openfdtag(fd, TAG_SOCK, 1);
*fdtagp(tag) = sock;
closefdtag(tag);
buffd(fd);
return fd;
}
static int
sys_sendto(int fd, void *data, int len, ulong, uchar *, int)
{
Socket *sock;
void *tag;
int ret;
DPRINT("sendto(%d, 0x%p, %d, ...)...", fd, data, len);
/*
print("sending %d data:\n", len);
if(len > 0){
write(1, data, len);
print("\n<<<<END\n");
}
*/
if((tag = openfdtag(fd, TAG_SOCK, 0))==nil)
return -ENOTSOCK;
sock = *fdtagp(tag);
ret = _write(sock->fd, data, len);
closefdtag(tag);
return ret;
}
static int
sys_recvfrom(int fd, void *data, int len, ulong flags, uchar *addr, int addrlen)
{
Socket *sock;
void *tag;
int ret;
DPRINT("recvfrom(%d, 0x%p, %d, 0x%lux, 0x%p, %d)...", fd, data, len, flags, addr, addrlen);
if((tag = openfdtag(fd, TAG_SOCK, 0))==nil)
return -ENOTSOCK;
sock = *fdtagp(tag);
if(flags & 2){
// peek!
buffd(sock->fd);
ret = peekbuffd(sock->fd, data, len, 0);
} else {
ret = _read(sock->fd, data, len);
}
/* print("recved %d data:\n", ret);
if(ret > 0){
write(1, data, ret);
print("\n<<<<END\n");
}
*/
if(addr){
int n;
n = sock->naddr;
memcpy(addr, sock->addr, n);
}
closefdtag(tag);
return ret;
}
static int
getsockaddr(Socket *sock, int remote, uchar *addr)
{
char buf[80];
char *dig[5];
uchar *a;
int fd;
int n;
DPRINT("getsockaddr()...");
if(sock->family != AF_INET)
return -1;
snprint(buf, sizeof(buf), "%s/%s", sock->name, remote?"remote":"local");
if((fd = open(buf, OREAD)) < 0){
return -1;
}
if((n = read(fd, buf, sizeof(buf)-1)) < 0){
close(fd);
return -1;
}
close(fd);
buf[n] = 0;
if(gettokens(buf, dig, 5, ".!")!=5)
return -1;
#define INT16(x) {a[1] = ((x)&0xFF00)>>8; a[0] = ((x)&0xFF); a += 2;}
#define INT8(x) {*a++ = ((x)&0xFF);}
a = addr;
INT16(AF_INET);
INT16(atoi(dig[4]));
INT8(atoi(dig[0]));
INT8(atoi(dig[1]));
INT8(atoi(dig[2]));
INT8(atoi(dig[3]));
n = (int)(a - addr);
#undef INT16
#undef INT8
return n;
}
static int
sys_getsockname(int fd, uchar *addr, int *paddrlen)
{
Socket *sock;
void *tag;
int ret;
DPRINT("getsockname(%d, ...)...", fd);
if((tag = openfdtag(fd, TAG_SOCK, 0))==nil)
return -ENOTSOCK;
sock = *fdtagp(tag);
ret = sock->naddr;
memcpy(addr, sock->addr, sock->naddr);
*paddrlen = sock->naddr;
// if((ret = getsockaddr(sock, 0, addr)) > 0)
// *paddrlen = ret;
closefdtag(tag);
return ret;
}
static int
sys_getpeername(int fd, uchar *addr, int *paddrlen)
{
Socket *sock;
void *tag;
int ret;
DPRINT("getpeername(%d, ...)...", fd);
if((tag = openfdtag(fd, TAG_SOCK, 0))==nil)
return -ENOTSOCK;
sock = *fdtagp(tag);
if((ret = getsockaddr(sock, 1, addr)) > 0)
*paddrlen = ret;
closefdtag(tag);
return ret;
}
static int
sys_shutdown(int fd, int how)
{
Socket *sock;
void *tag;
int ret;
DPRINT("shutdown(%d, %d)...\n", fd, how);
if((tag = openfdtag(fd, TAG_SOCK, 0))==nil)
return -ENOTSOCK;
sock = *fdtagp(tag);
ret = 0;
if((how==SHUT_RDWR) && (sock->stype==SOCK_STREAM)){
int cfd;
char name[80];
snprint(name, sizeof(name), "%s/ctl", sock->name);
if((cfd = open(name, OWRITE)) < 0){
ret = mkerror();
goto out;
}
fprint(cfd, "hangup");
close(cfd);
}
shutdownbuffd(fd, how);
out:
closefdtag(tag);
return ret;
}
enum{
SYS_SOCKET=1,
SYS_BIND,
SYS_CONNECT,
SYS_LISTEN,
SYS_ACCEPT,
SYS_GETSOCKNAME,
SYS_GETPEERNAME,
SYS_SOCKETPAIR,
SYS_SEND,
SYS_RECV,
SYS_SENDTO,
SYS_RECVFROM,
SYS_SHUTDOWN,
SYS_SETSOCKOPT,
SYS_GETSOCKOPT,
SYS_SENDMSG,
SYS_RECVMSG,
};
SYSCALL(sys_socketcall)
{
int call;
ulong *a;
int ret;
call = (int)ARG1;
a = (ulong*)ARG2;
#define _INT(x) (int)a[x]
#define _PTR(x) (void*)a[x]
switch(call){
case SYS_SOCKET:
ret = sys_socket(_INT(0), _INT(1), _INT(2));
break;
case SYS_CONNECT:
ret = sys_connect(_INT(0), _PTR(1), _INT(2));
break;
case SYS_SENDTO:
ret = sys_sendto(_INT(0), _PTR(1), _INT(2), a[3], _PTR(4), _INT(5));
break;
case SYS_RECVFROM:
ret = sys_recvfrom(_INT(0), _PTR(1), _INT(2), a[3], _PTR(4), _INT(5));
break;
case SYS_SEND:
ret = sys_sendto(_INT(0), _PTR(1), _INT(2), a[3], nil, 0);
break;
case SYS_RECV:
ret = sys_recvfrom(_INT(0), _PTR(1), _INT(2), a[3], nil, 0);
break;
case SYS_GETSOCKNAME:
ret = sys_getsockname(_INT(0), _PTR(1), _PTR(2));
break;
case SYS_GETPEERNAME:
ret = sys_getpeername(_INT(0), _PTR(1), _PTR(2));
break;
case SYS_SHUTDOWN:
ret = sys_shutdown(_INT(0), _INT(1));
break;
case SYS_BIND:
ret = sys_bind(_INT(0), _PTR(1), _INT(2));
break;
case SYS_LISTEN:
ret = sys_listen(_INT(0));
break;
case SYS_ACCEPT:
ret = sys_accept(_INT(0), _PTR(1), _PTR(2));
break;
case SYS_SOCKETPAIR:
case SYS_SETSOCKOPT:
case SYS_GETSOCKOPT:
ret = 0;
break;
case SYS_SENDMSG:
case SYS_RECVMSG:
default:
fprint(2, "socketcall %d not implemented...", call);
ret = -1;
}
#undef _INT
#undef _PTR
RETURN(ret);
}
|