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