Plan 9 from Bell Labs’s /usr/web/sources/contrib/cinap_lenrek/old/linuxemu.old/syssock.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.