view interps/cfunge/cfunge-src/src/fingerprints/SOCK/SOCK.c @ 12518:2d8fe55c6e65 draft default tip

<int-e> learn The password of the month is release incident pilot.
author HackEso <hackeso@esolangs.org>
date Sun, 03 Nov 2024 00:31:02 +0000
parents 859f9b4339e6
children
line wrap: on
line source

/* -*- mode: C; coding: utf-8; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
 *
 * cfunge - A standard-conforming Befunge93/98/109 interpreter in C.
 * Copyright (C) 2008-2009 Arvid Norlander <anmaster AT tele2 DOT se>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at the proxy's option) any later version. Arvid Norlander is a
 * proxy who can decide which future versions of the GNU General Public
 * License can be used.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

// It need to get it's own typedefs from the header.
#define FUNGE_EXTENDS_SOCK

#include "SOCK.h"
#include "../../stack.h"

#include <unistd.h> /* close, fcntl */
#include <fcntl.h>  /* fcntl */

#include <sys/types.h>  /* accept, connect, socket, ... */
#include <sys/socket.h> /* accept, connect, socket, ... */
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>  /* htons */
#include <netdb.h>

// Based on how CCBI does it.

typedef union {
	struct sockaddr_in in;
	struct sockaddr    gen;
} FungeSockAddr;

#define ALLOCCHUNK 2
// Array of pointers
static FungeSocketHandle** sockets = NULL;
static size_t maxHandle = 0;

/// Used by allocate_handle() below to find next free handle.
FUNGE_ATTR_FAST FUNGE_ATTR_WARN_UNUSED
static inline funge_cell findNextfree_handle(void)
{
	for (size_t i = 0; i < maxHandle; i++) {
		if (sockets[i] == NULL)
			return (funge_cell)i;
	}
	// No free one, extend array..
	{
		FungeSocketHandle** newlist = (FungeSocketHandle**)cf_realloc(sockets, (maxHandle + ALLOCCHUNK) * sizeof(FungeSocketHandle*));
		if (!newlist)
			return -1;
		sockets = newlist;
		for (size_t i = maxHandle; i < (maxHandle + ALLOCCHUNK); i++)
			sockets[i] = NULL;
		maxHandle += ALLOCCHUNK;
		return (funge_cell)(maxHandle - ALLOCCHUNK);
	}
}

/// Get a new handle to use for a file, also allocates buffer for it.
/// @return Handle, or -1 on failure
FUNGE_ATTR_FAST FUNGE_ATTR_WARN_UNUSED
static inline funge_cell allocate_handle(void)
{
	funge_cell h;

	h = findNextfree_handle();
	if (h < 0)
		return -1;

	sockets[h] = cf_malloc(sizeof(FungeSocketHandle));
	if (!sockets[h])
		return -1;
	return h;
}

/// Free a handle. close() the file before calling this.
FUNGE_ATTR_FAST
static inline void free_handle(funge_cell h)
{
	if (!sockets[h])
		return;
	cf_free(sockets[h]);
	sockets[h] = NULL;
}

/// Checks if handle is valid.
FUNGE_ATTR_FAST FUNGE_ATTR_WARN_UNUSED
static inline bool valid_handle(funge_cell h)
{
	if ((h < 0) || ((size_t)h >= maxHandle) || (!sockets[h])) {
		return false;
	} else {
		return true;
	}
}

FUNGE_ATTR_FAST FUNGE_ATTR_WARN_UNUSED
FungeSocketHandle* finger_SOCK_LookupHandle(funge_cell h)
{
	if (!valid_handle(h))
		return NULL;
	return sockets[h];
}


static inline int popFam(instructionPointer * ip)
{
	switch (stack_pop(ip->stack)) {
		case 1:  return AF_UNIX;
		case 2:  return AF_INET;
		default: return AF_UNSPEC;
	}
}


/// A - Accept a connection
static void finger_SOCK_accept(instructionPointer * ip)
{
	funge_cell s = stack_pop(ip->stack);

	if (!valid_handle(s))
		goto error;

	{
		FungeSockAddr addr;
		socklen_t addrlen = sizeof(addr.in);
		int as;
		funge_cell i;

		addr.in.sin_addr.s_addr = 0;
		addr.in.sin_port = 0;
		addr.in.sin_family = AF_INET;

		as = accept(sockets[s]->fd, &addr.gen, &addrlen);
		if (as == -1)
			goto error;

		fcntl(as, F_SETFD, FD_CLOEXEC, 1);

		i = allocate_handle();
		if (i == -1)
			goto error;
		sockets[i]->fd = as;
		sockets[i]->family = sockets[s]->family;

		stack_push(ip->stack, addr.in.sin_port);
		stack_push(ip->stack, (funge_cell)addr.in.sin_addr.s_addr);
		stack_push(ip->stack, i);
	}
	return;
error:
	ip_reverse(ip);
}

/// B - Bind a socket
static void finger_SOCK_bind(instructionPointer * ip)
{
	uint32_t  address = (uint32_t)stack_pop(ip->stack);
	uint16_t  port    = (uint16_t)stack_pop(ip->stack);
	int       fam     = popFam(ip);
	funge_cell s       = stack_pop(ip->stack);
	FungeSockAddr addr;

	if (!valid_handle(s))
		goto error;

	switch (fam) {
		case AF_INET: {
			int retval;

			addr.in.sin_family = AF_INET;
			addr.in.sin_addr.s_addr = address;
			addr.in.sin_port = htons(port);

			retval = bind(sockets[s]->fd, &addr.gen, sizeof(addr.in));
			if (retval == -1)
				goto error;
			break;
		}
		default: goto error;
	}
	return;
error:
	ip_reverse(ip);
}

/// C - Open a connection
static void finger_SOCK_open(instructionPointer * ip)
{
	uint32_t  address = (uint32_t)stack_pop(ip->stack);
	uint16_t  port    = (uint16_t)stack_pop(ip->stack);
	int       fam     = popFam(ip);
	funge_cell s       = stack_pop(ip->stack);
	FungeSockAddr addr;

	if (!valid_handle(s))
		goto error;

	switch (fam) {
		case AF_INET: {
			int retval;

			addr.in.sin_family = AF_INET;
			addr.in.sin_addr.s_addr = address;
			addr.in.sin_port = htons(port);

			retval = connect(sockets[s]->fd, &addr.gen, sizeof(addr.in));
			if (retval == -1)
				goto error;

			break;
		}
		default: goto error;
	}
	return;
error:
	ip_reverse(ip);
}

/// I - Convert an ASCII IP address to a 32 bit address
static void finger_SOCK_fromascii(instructionPointer * ip)
{
	char * restrict str;
	struct in_addr addr;

	str = (char*)stack_pop_string(ip->stack, NULL);
	if (inet_pton(AF_INET, str, &addr) != 1) {
		ip_reverse(ip);
	} else {
		stack_push(ip->stack, (funge_cell)addr.s_addr);
	}
	stack_free_string(str);

}

/// K - Kill a connection
static void finger_SOCK_kill(instructionPointer * ip)
{
	funge_cell s       = stack_pop(ip->stack);
	if (!valid_handle(s))
		goto invalid;
	shutdown(sockets[s]->fd, SHUT_RDWR);
	if (close(sockets[s]->fd) == -1) {
		goto error;
	}
	free_handle(s);
	return;
error:
	free_handle(s);
invalid:
	ip_reverse(ip);
}

/// L - Set a socket to listening mode (n=backlog size)
static void finger_SOCK_listen(instructionPointer * ip)
{
	funge_cell s = stack_pop(ip->stack);
	int n = (int)stack_pop(ip->stack);

	if (!valid_handle(s))
		goto error;

	if (listen(sockets[s]->fd, n) == -1)
		goto error;
	return;
error:
	ip_reverse(ip);
}

/// O - Set socket option
static void finger_SOCK_setopt(instructionPointer * ip)
{
	int val;
	int o;

	funge_cell s = stack_pop(ip->stack);
	funge_cell t = stack_pop(ip->stack);

	val = (int)stack_pop(ip->stack);

	if (!valid_handle(s))
		goto error;

	switch (t) {
		case 1: o = SO_DEBUG;     break;
		case 2: o = SO_REUSEADDR; break;
		case 3: o = SO_KEEPALIVE; break;
		case 4: o = SO_DONTROUTE; break;
		case 5: o = SO_BROADCAST; break;
		case 6: o = SO_OOBINLINE; break;
		default: goto error;
	}

	{
		int retval;
		retval = setsockopt(sockets[s]->fd, SOL_SOCKET, o, &val, sizeof(int));
		if (retval == -1)
			goto error;
	}
	return;
error:
	ip_reverse(ip);
}

/// R - Receive from a socket
static void finger_SOCK_receive(instructionPointer * ip)
{
	unsigned char *buffer = NULL;
	ssize_t got;
	funge_cell s   = stack_pop(ip->stack);
	funge_cell len = stack_pop(ip->stack);

	funge_vector v = stack_pop_vector(ip->stack);

	if (len < 0)
		goto error;

	if (!valid_handle(s))
		goto error;

	v.x += ip->storageOffset.x;
	v.y += ip->storageOffset.y;

	buffer = cf_malloc_noptr((size_t)len * sizeof(unsigned char));

	got = recv(sockets[s]->fd, buffer, (size_t)len, 0);

	stack_push(ip->stack, (funge_cell)got);

	if (got == -1)
		goto error;

	for (ssize_t i = 0; i < got; ++i)
		fungespace_set(buffer[i], vector_create_ref(v.x + i, v.y));

	goto end;
error:
	ip_reverse(ip);
end:
	if (buffer)
		cf_free(buffer);
}

/// S - Create a socket
static void finger_SOCK_create(instructionPointer * ip)
{
	int type;
	int fam;
	// Protocol.
	stack_discard(ip->stack, 1);

	switch (stack_pop(ip->stack)) {
		case 1: type = SOCK_DGRAM ; break;
		case 2: type = SOCK_STREAM ; break;
		default: ip_reverse(ip); return;
	}

	fam = popFam(ip);

	if (fam == AF_UNSPEC)
		goto error;

	{
		funge_cell h = allocate_handle();
		if (h == -1) {
			goto error;
		}

		sockets[h]->fd = socket(fam, type, 0);
		if (sockets[h]->fd == -1)
			goto error;

		fcntl(sockets[h]->fd, F_SETFD, FD_CLOEXEC, 1);

		sockets[h]->family = fam;

		stack_push(ip->stack, h);
	}
	return;
error:
	ip_reverse(ip);
}

/// W - Write to a socket
static void finger_SOCK_write(instructionPointer * ip)
{
	unsigned char *buffer = NULL;
	ssize_t sent;
	funge_cell s   = stack_pop(ip->stack);
	funge_cell len = stack_pop(ip->stack);

	funge_vector v = stack_pop_vector(ip->stack);

	if (len < 0)
		goto error;

	if (!valid_handle(s))
		goto error;

	v.x += ip->storageOffset.x;
	v.y += ip->storageOffset.y;

	buffer = cf_malloc_noptr((size_t)len * sizeof(unsigned char));

	for (size_t i = 0; i < (size_t)len; ++i)
		buffer[i] = (unsigned char)fungespace_get(vector_create_ref(v.x + (funge_cell)i, v.y));

	sent = send(sockets[s]->fd, buffer, (size_t)len, 0);

	stack_push(ip->stack, (funge_cell)sent);

	if (sent == -1)
		goto error;

	goto end;
error:
	ip_reverse(ip);
end:
	if (buffer)
		cf_free(buffer);
}

FUNGE_ATTR_FAST static inline bool init_handle_list(void)
{
	sockets = (FungeSocketHandle**)cf_calloc(ALLOCCHUNK, sizeof(FungeSocketHandle*));
	if (!sockets)
		return false;
	maxHandle = ALLOCCHUNK;
	return true;
}

bool finger_SOCK_load(instructionPointer * ip)
{
	if (!sockets)
		if (!init_handle_list())
			return false;

	manager_add_opcode(SOCK, 'A', accept)
	manager_add_opcode(SOCK, 'B', bind)
	manager_add_opcode(SOCK, 'C', open)
	manager_add_opcode(SOCK, 'I', fromascii)
	manager_add_opcode(SOCK, 'K', kill)
	manager_add_opcode(SOCK, 'L', listen)
	manager_add_opcode(SOCK, 'O', setopt)
	manager_add_opcode(SOCK, 'R', receive)
	manager_add_opcode(SOCK, 'S', create)
	manager_add_opcode(SOCK, 'W', write)
	return true;
}