From 816ffa2b5da1a664b543a45c374f1b24406bb133 Mon Sep 17 00:00:00 2001 From: Justin Meza Date: Mon, 5 May 2014 01:46:00 -0400 Subject: [PATCH] added SOCKS library for socket I/O --- CMakeLists.txt | 2 + binding.c | 146 ++++++++- inet.c | 304 ++++++++++++++++++ inet.h | 113 +++++++ .../13-Bindings/3-socket/1-lookup/test.lol | 6 + .../13-Bindings/3-socket/1-lookup/test.out | 1 + .../13-Bindings/3-socket/2-openclose/test.lol | 8 + .../13-Bindings/3-socket/2-openclose/test.out | 1 + .../13-Bindings/3-socket/3-server/test.lol | 11 + 9 files changed, 584 insertions(+), 8 deletions(-) create mode 100644 inet.c create mode 100644 inet.h create mode 100644 test/1.4-Tests/13-Bindings/3-socket/1-lookup/test.lol create mode 100644 test/1.4-Tests/13-Bindings/3-socket/1-lookup/test.out create mode 100644 test/1.4-Tests/13-Bindings/3-socket/2-openclose/test.lol create mode 100644 test/1.4-Tests/13-Bindings/3-socket/2-openclose/test.out create mode 100644 test/1.4-Tests/13-Bindings/3-socket/3-server/test.lol diff --git a/CMakeLists.txt b/CMakeLists.txt index a34d435..99ccb02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ SET(HDRS unicode.h error.h binding.h + inet.h ) SET(SRCS @@ -40,6 +41,7 @@ SET(SRCS unicode.c error.c binding.c + inet.c ) add_executable(lci ${SRCS} ${HDRS}) diff --git a/binding.c b/binding.c index 8de6b18..9f713ef 100644 --- a/binding.c +++ b/binding.c @@ -1,4 +1,5 @@ #include "binding.h" +#include "inet.h" /* for SOCKS */ ValueObject *getArg(struct scopeobject *scope, char *name) { @@ -8,6 +9,102 @@ ValueObject *getArg(struct scopeobject *scope, char *name) return val; } +ReturnObject *iopenWrapper(struct scopeobject *scope) +{ + ValueObject *arg1 = getArg(scope, "addr"); + ValueObject *arg2 = getArg(scope, "port"); + char *addr = getString(arg1); + int port = getInteger(arg2); + + inet_host_t *h = malloc(sizeof(inet_host_t)); + inet_open(h, IN_PROT_TCP, addr, port); + + ValueObject *ret = createBlobValueObject(h); + return createReturnObject(RT_RETURN, ret); +} + +ReturnObject *ilookupWrapper(struct scopeobject *scope) +{ + ValueObject *arg1 = getArg(scope, "addr"); + char *addr = getString(arg1); + + char *h = inet_lookup(addr); + + ValueObject *ret = createStringValueObject(h); + return createReturnObject(RT_RETURN, ret); +} + +ReturnObject *iacceptWrapper(struct scopeobject *scope) +{ + ValueObject *arg1 = getArg(scope, "local"); + inet_host_t *host = (inet_host_t *)getBlob(arg1); + + inet_host_t *h = malloc(sizeof(inet_host_t)); + inet_accept(h, host); + + ValueObject *ret = createBlobValueObject(h); + return createReturnObject(RT_RETURN, ret); +} + +ReturnObject *iconnectWrapper(struct scopeobject *scope) +{ + ValueObject *arg1 = getArg(scope, "local"); + ValueObject *arg2 = getArg(scope, "addr"); + ValueObject *arg3 = getArg(scope, "port"); + inet_host_t *host = (inet_host_t *)getBlob(arg1); + char *addr = getString(arg2); + int port = getInteger(arg3); + + inet_host_t *h = malloc(sizeof(inet_host_t)); + inet_setup(h, IN_PROT_TCP, addr, port); + inet_connect(host, h); + + ValueObject *ret = createBlobValueObject(h); + return createReturnObject(RT_RETURN, ret); +} + +ReturnObject *icloseWrapper(struct scopeobject *scope) +{ + ValueObject *arg1 = getArg(scope, "local"); + inet_host_t *host = (inet_host_t *)getBlob(arg1); + + inet_close(host); + + ValueObject *ret = createBlobValueObject(host); + return createReturnObject(RT_RETURN, ret); +} + +ReturnObject *isendWrapper(struct scopeobject *scope) +{ + ValueObject *arg1 = getArg(scope, "local"); + ValueObject *arg2 = getArg(scope, "remote"); + ValueObject *arg3 = getArg(scope, "data"); + inet_host_t *local = (inet_host_t *)getBlob(arg1); + inet_host_t *remote = (inet_host_t *)getBlob(arg2); + char *data = getString(arg3); + + int n = inet_send(local, remote, data, strlen(data)); + + ValueObject *ret = createIntegerValueObject(n); + return createReturnObject(RT_RETURN, ret); +} + +ReturnObject *ireceiveWrapper(struct scopeobject *scope) +{ + ValueObject *arg1 = getArg(scope, "local"); + ValueObject *arg2 = getArg(scope, "remote"); + ValueObject *arg3 = getArg(scope, "amount"); + inet_host_t *local = (inet_host_t *)getBlob(arg1); + inet_host_t *remote = (inet_host_t *)getBlob(arg2); + int amount = getInteger(arg3); + + char *data = malloc(sizeof(char) * amount); + inet_receive(remote, local, data, amount, -1); + + ValueObject *ret = createStringValueObject(data); + return createReturnObject(RT_RETURN, ret); +} + ReturnObject *fopenWrapper(struct scopeobject *scope) { ValueObject *arg1 = getArg(scope, "filename"); @@ -58,6 +155,16 @@ ReturnObject *fcloseWrapper(struct scopeobject *scope) return createReturnObject(RT_DEFAULT, NULL); } +ReturnObject *rewindWrapper(struct scopeobject *scope) +{ + ValueObject *arg1 = getArg(scope, "file"); + FILE *file = (FILE *)getBlob(arg1); + + rewind(file); + + return createReturnObject(RT_DEFAULT, NULL); +} + ReturnObject *strlenWrapper(struct scopeobject *scope) { ValueObject *arg1 = getArg(scope, "string"); @@ -100,10 +207,11 @@ void loadLibrary(ScopeObject *scope, IdentifierNode *target) lib = createScopeObject(scope); if (!lib) goto loadLibraryAbort; - loadBinding(lib, "FOPENIN", "filename mode", &fopenWrapper); - loadBinding(lib, "FREADIN", "file length", &freadWrapper); - loadBinding(lib, "FWRITIN", "file data", &fwriteWrapper); - loadBinding(lib, "FCLOSIN", "file", &fcloseWrapper); + loadBinding(lib, "OPEN", "filename mode", &fopenWrapper); + loadBinding(lib, "LUK", "file length", &freadWrapper); + loadBinding(lib, "SCRIBBEL", "file data", &fwriteWrapper); + loadBinding(lib, "AGEIN", "file", &rewindWrapper); + loadBinding(lib, "CLOSE", "file", &fcloseWrapper); id = createIdentifierNode(IT_DIRECT, (void *)copyString("STDIO"), NULL, NULL, 0); if (!id) goto loadLibraryAbort; @@ -116,13 +224,35 @@ void loadLibrary(ScopeObject *scope, IdentifierNode *target) if (!updateScopeValue(scope, scope, id, val)) goto loadLibraryAbort; deleteIdentifierNode(id); - } - else if (!strcmp(name, "STRING")) { + } else if (!strcmp(name, "SOCKS")) { lib = createScopeObject(scope); if (!lib) goto loadLibraryAbort; - loadBinding(lib, "STRLENIN", "string", &strlenWrapper); - loadBinding(lib, "STRATIN", "string position", &stratWrapper); + loadBinding(lib, "RESOLV", "addr", &ilookupWrapper); + loadBinding(lib, "BIND", "addr port", &iopenWrapper); + loadBinding(lib, "LISTN", "local timeout", &iacceptWrapper); + loadBinding(lib, "KONN", "local addr port", &iconnectWrapper); + loadBinding(lib, "CLOSE", "local", &icloseWrapper); + loadBinding(lib, "PUT", "local remote data", &isendWrapper); + loadBinding(lib, "GET", "local remote amount", &ireceiveWrapper); + + id = createIdentifierNode(IT_DIRECT, (void *)copyString("SOCKS"), NULL, NULL, 0); + if (!id) goto loadLibraryAbort; + + if (!createScopeValue(scope, scope, id)) goto loadLibraryAbort; + + val = createArrayValueObject(lib); + if (!val) goto loadLibraryAbort; + lib = NULL; + + if (!updateScopeValue(scope, scope, id, val)) goto loadLibraryAbort; + deleteIdentifierNode(id); + } else if (!strcmp(name, "STRING")) { + lib = createScopeObject(scope); + if (!lib) goto loadLibraryAbort; + + loadBinding(lib, "LEN", "string", &strlenWrapper); + loadBinding(lib, "AT", "string position", &stratWrapper); id = createIdentifierNode(IT_DIRECT, (void *)copyString("STRING"), NULL, NULL, 0); if (!id) goto loadLibraryAbort; diff --git a/inet.c b/inet.c new file mode 100644 index 0000000..a6367cf --- /dev/null +++ b/inet.c @@ -0,0 +1,304 @@ +#include "inet.h" + +// inet_setup (TCP:client / UDP:client) +// +// Sets up an inet_host structure. +void +inet_setup(inet_host_t *host, + int protocol, + const char *addr, + unsigned short port) +{ + // Set up our inet_host structure + host->protocol = protocol; + + // Set up our sockaddr_in structure + host->addr.sin_family = AF_INET; + host->addr.sin_port = htons(port); + host->addr.sin_addr.s_addr = + (addr == IN_ADDR_ANY ? IN_ADDR_ANY : inet_addr(addr)); + memset(host->addr.sin_zero, 0, sizeof(host->addr.sin_zero)); +} + +// inet_open (TCP / UDP) +// +// Prepares `host' for transmission. +// +// `protocol' specifies the protocol to use for the transmission and is either +// `IN_PROT_TCP' or `IN_PROT_UDP'. +// +// `addr' is the IP address the host session structure will use: either a string +// of the form `X.X.X.X', where each `X' is the textual representation of a +// number in the range 0 to 255, or `IN_ADDR_ANY' to specify the current host's +// IP address. +// +// `port' is the port number to bind to on the host machine. Specifying +// IN_PORT_ANY attempts to use any available port assigned by the kernel. Use +// this option when the port your host structure is bound to does not matter. +// +// Returns one of: +// 0 Success. +// -EIN_SOCK Error acquiring socket file descriptor. +// -EIN_BIND Error binding socket to port. +int +inet_open(inet_host_t *host, + int protocol, + const char *addr, + unsigned short port) +{ + // If we are not able to get a socket, fail with a socket error + host->fd = socket(PF_INET, protocol, 0); + if (host->fd < 0) { + perror("Error acquiring socket file descriptor!\n"); + return -EIN_SOCK; + } + + // Set up our sockaddr_in structure + inet_setup(host, protocol, addr, port); + + // If we are not able to bind to a port, fail with a bind error + if (bind(host->fd, (struct sockaddr *)&(host->addr), + sizeof(host->addr)) < 0) { + perror("Error acquiring socket file descriptor!\n"); + return -EIN_BIND; + } + + // Set up our host protocol + host->protocol = protocol; + + return 0; +} + +// inet_accept (TCP:server) +// +// Accepts an incoming connection from the `remote' host to the `local' host. +// +// Returns one of: +// 0 Success. +// -EIN_PROT Incorrect protocol. +// -EIN_LSTN Error listening. +// -EIN_ACPT Error accepting connection. +int +inet_accept(inet_host_t *remote, + inet_host_t *local) +{ + socklen_t len = sizeof(remote->addr); + + // Make sure we only accept TCP sessions + if (local->protocol != IN_PROT_TCP) { + fprintf(stderr, "Error accepting from non-TCP protocol!\n"); + return -EIN_PROT; + } + + // Listen for an acceptable connection + if (listen(local->fd, IN_BACKLOG) < 0) { + perror("Error encountered while listening!\n"); + return -EIN_LSTN; + } + + // Accept the connection + remote->fd = accept(local->fd, (struct sockaddr *)&(remote->addr), &len); + if (remote->fd < 0) { + perror("Error accepting connection!\n"); + return -EIN_ACPT; + } + + return 0; +} + +// inet_connect (TCP:client) +// +// Connects the `local' host to the `remote' host. +// +// Returns one of: +// 0 Success. +// -EIN_PROT Incorrect protocol. +// -EIN_CONN Error connecting to port. +int +inet_connect(inet_host_t *local, + inet_host_t *remote) +{ + // Make sure we only connect TCP sessions + if (local->protocol != IN_PROT_TCP) { + fprintf(stderr, "Error accepting from non-TCP protocol!\n"); + return -EIN_PROT; + } + + // Connect to remote host + if (connect(local->fd, (struct sockaddr *)&(remote->addr), sizeof(remote->addr)) < 0) { + perror("Error connecting to remote host!\n"); + return -EIN_CONN; + } + + // Link `local->fd' and `remote->fd' on the client because `inet_send' always + // sends data through `remote->fd' for TCP connections (this makes sense + // intuitively, as one would send data *to* a remote address). However, a TCP + // client (which would be the only host to call this function) always + // communicates through it's `local->fd' and leaves `remote->fd' unused so we + // may safely repurpose it for the sake of continuity. + remote->fd = local->fd; + + return 0; +} + +// inet_receive (TCP / UDP) +// +// Receives a `len' bytes sent from `remote' to `local' and stores them in +// `data'. If no message is received within `timeout' seconds, -EIN_TIME is +// returned. +// +// Setting `timeout' to 0 causes the function to return immediately if there is +// no data to receive and setting `timeout' to -1 causes the function to block +// until data is ready to be received. +// +// Returns one of: +// Number of bytes received Success. +// -EIN_TIME Timeout. +// -EIN_RECV Error receiving data. +int +inet_receive(inet_host_t *remote, + inet_host_t *local, + void *data, + int len, + int timeout) +{ + fd_set fds; + struct timeval time; + int size; + + // Set the timeout interval + time.tv_sec = timeout; + time.tv_usec = 0; + + // Initialize the file descriptor set + FD_ZERO(&fds); + + // Depending on the protocol, receive data + switch (local->protocol) { + case IN_PROT_TCP: + FD_SET(remote->fd, &fds); + // Block for a specified amount of time + if (timeout != -1) + select(remote->fd + 1, &fds, NULL, NULL, &time); + else + select(remote->fd + 1, &fds, NULL, NULL, NULL); + if (FD_ISSET(remote->fd, &fds)) { + size = recv(remote->fd, data, len, 0); + if (size < 0) { + perror("Error receiving data!\n"); + return -EIN_RECV; + } + } + else + return -EIN_TIME; + break; + case IN_PROT_UDP: { + socklen_t n = sizeof(remote->addr); + FD_SET(local->fd, &fds); + // Block for a specified amount of time + if (timeout != -1) + select(local->fd + 1, &fds, NULL, NULL, &time); + else + select(local->fd + 1, &fds, NULL, NULL, NULL); + if (FD_ISSET(local->fd, &fds)) { + size = recvfrom(local->fd, data, len, 0, + (struct sockaddr *)&(remote->addr), &n); + if (size < 0) { + perror("Error receiving data!\n"); + return -EIN_RECV; + } + } + else + return -EIN_TIME; + break; + } + } + + return size; +} + +// inet_send (TCP / UDP) +// +// Sends `len' bytes of `data' from `local' to `remote'. +// +// Returns one of: +// Number of bytes sent Success. +// -EIN_SEND Error sending data. +int +inet_send(inet_host_t *local, + inet_host_t *remote, + void *data, + int len) +{ + int size; + + // Depending on the protocol, send data + switch (local->protocol) { + case IN_PROT_TCP: + size = send(remote->fd, data, len, 0); + if (size < 0) { + perror("Error sending data!\n"); + return -EIN_SEND; + } + break; + case IN_PROT_UDP: { + size = sendto(local->fd, data, len, 0, + (struct sockaddr *)&(remote->addr), sizeof(remote->addr)); + if (size < 0) { + perror("Error sending data!\n"); + return -EIN_SEND; + } + break; + } + } + + return size; +} + +// inet_close (TCP / UDP) +// +// Closes an inet connection. +// +// Returns one of: +// 0 Success. +// -1 Invalid `host' pointer. +int +inet_close(inet_host_t *host) +{ + // Sanity check + if (!host) return -1; + + // Close the file descriptor + if (host->fd) close(host->fd); + + return 0; +} + +// inet_lookup +// +// Looks up information by resolving a DNS name and returns a pointer to the +// resolved IP address. +// +// Returns one of: +// 0 Unable to resolve name. +// Pointer to resolved IP address Must be freed by caller. +char * +inet_lookup(const char *name) +{ + char *hostname; + char *temp; + struct hostent *ent; + + // If unable to get host by name, quit with lookup error + if (!(ent = gethostbyname(name))) { + herror("gethostbyname"); + return 0; + } + + // Copy the looked up name + temp = inet_ntoa(*((struct in_addr *)ent->h_addr)); + hostname = malloc(sizeof(char) * (strlen(temp) + 1)); + strcpy(hostname, temp); + + return hostname; +} diff --git a/inet.h b/inet.h new file mode 100644 index 0000000..ea266e7 --- /dev/null +++ b/inet.h @@ -0,0 +1,113 @@ +// inet.h +// A library for simplifying networked communication. +// 2008-4-27 +// +// MAINTAINER +// +// Justin J. Meza +// +// LICENSE +// +// You are free to use, modify, and re-distribute this software provided +// that you make any modifications publicly available. Additionally, this +// software is provided without any warranty; use of this software is at the +// user's own risk. The author disclaims any liability for malfunctions as a +// result of this software or any derivative works produced with it. +// +// Please report and bugs, fixes, and improvements to the maintainer. +// +// This library allows two `host's (specifically, two processes) to connect and +// exchange data in a session using either TCP or UDP. +// +// The following two diagrams list the general order of function calls required +// to handle a session for TCP and UDP. The order of function calls in `SETUP' +// must be strictly followed; `TRANSMISSION' and `TEARDOWN' function calls may +// be done in any order and some examples are listed. +// +// Functions are labeled in the form ([TCP]:[client|server] / +// [UDP]:[client|server]) to denote whether the function is meant to be used by +// TCP, UDP, or either connection type and furthermore whether only clients, +// servers, or either are meant to call the function. +// +// +===========================================+ +// | TCP | +// +=====================+=====================+ +// | SERVER | CLIENT | +// +---------------------+---------------------+ +// | inet_open | inet_open |\ \ +// | inet_accept | inet_setup | | SETUP | +// | | inet_connect |/ | +// | | | | +// | inet_receive | inet_send |\ TRANSMISSION > SESSION +// | inet_send | inet_receive |/ | +// | | | | +// | inet_close(local) | inet_close(local) |\ TEARDOWN | +// | inet_close(remote) | |/ / +// +---------------------+---------------------+ +// +// +===========================================+ +// | UDP | +// +=====================+=====================+ +// | SERVER | CLIENT | +// +---------------------+---------------------+ +// | inet_open | inet_open |\ SETUP \ +// | | inet_setup |/ | +// | | | | +// | inet_receive | inet_send |\ TRANSMISSION > SESSION +// | inet_send | inet_receive |/ | +// | | | | +// | inet_close | inet_close |> TEARDOWN / +// +---------------------+---------------------+ + +#ifndef __INET_H__ +#define __INET_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IN_PORT_ANY 0 +#define IN_ADDR_ANY INADDR_ANY + +#define IN_PROT_TCP SOCK_STREAM +#define IN_PROT_UDP SOCK_DGRAM + +#define IN_BACKLOG 10 // Number of simultaneous incoming connections to allow +#define IN_HOSTLEN 64 // + +// Set up our error codes +typedef enum err_code { + EIN_SOCK = 2, // Error acquiring socket file descriptor + EIN_BIND, // Error binding socket to port + EIN_CONN, // Error connecting to port + EIN_PROT, // Incorrect protocol + EIN_LSTN, // Error listening + EIN_ACPT, // Error accepting connection + EIN_SEND, // Error sending data + EIN_RECV, // Error receiving data + EIN_TIME, // Timeout +} err_code_t; + +// Structure representing an internet host +typedef struct inet_host { + int fd; + int protocol; + struct sockaddr_in addr; +} inet_host_t; + +void inet_setup(inet_host_t *, int, const char *, unsigned short); +int inet_open(inet_host_t *, int, const char *, unsigned short); +int inet_accept(inet_host_t *, inet_host_t *); +int inet_connect(inet_host_t *, inet_host_t *); +int inet_receive(inet_host_t *, inet_host_t *, void *, int, int); +int inet_send(inet_host_t *, inet_host_t *, void *, int); +int inet_close(inet_host_t *); +char *inet_lookup(const char *); + +#endif // __INET_H__ diff --git a/test/1.4-Tests/13-Bindings/3-socket/1-lookup/test.lol b/test/1.4-Tests/13-Bindings/3-socket/1-lookup/test.lol new file mode 100644 index 0000000..7e66411 --- /dev/null +++ b/test/1.4-Tests/13-Bindings/3-socket/1-lookup/test.lol @@ -0,0 +1,6 @@ +HAI 1.4 + CAN HAS SOCKS? + I HAS A addr + addr R I IZ SOCKS'Z RESOLV YR "localhost" MKAY + VISIBLE addr +KTHXBYE diff --git a/test/1.4-Tests/13-Bindings/3-socket/1-lookup/test.out b/test/1.4-Tests/13-Bindings/3-socket/1-lookup/test.out new file mode 100644 index 0000000..7b9ad53 --- /dev/null +++ b/test/1.4-Tests/13-Bindings/3-socket/1-lookup/test.out @@ -0,0 +1 @@ +127.0.0.1 diff --git a/test/1.4-Tests/13-Bindings/3-socket/2-openclose/test.lol b/test/1.4-Tests/13-Bindings/3-socket/2-openclose/test.lol new file mode 100644 index 0000000..77e75d7 --- /dev/null +++ b/test/1.4-Tests/13-Bindings/3-socket/2-openclose/test.lol @@ -0,0 +1,8 @@ +HAI 1.4 + CAN HAS SOCKS? + I HAS A sock + I HAS A conn + sock R I IZ SOCKS'Z BIND YR "127.0.0.1" AN YR 13337 MKAY + I IZ SOCKS'Z CLOSE YR sock MKAY + VISIBLE "done" +KTHXBYE diff --git a/test/1.4-Tests/13-Bindings/3-socket/2-openclose/test.out b/test/1.4-Tests/13-Bindings/3-socket/2-openclose/test.out new file mode 100644 index 0000000..19f86f4 --- /dev/null +++ b/test/1.4-Tests/13-Bindings/3-socket/2-openclose/test.out @@ -0,0 +1 @@ +done diff --git a/test/1.4-Tests/13-Bindings/3-socket/3-server/test.lol b/test/1.4-Tests/13-Bindings/3-socket/3-server/test.lol new file mode 100644 index 0000000..bae627a --- /dev/null +++ b/test/1.4-Tests/13-Bindings/3-socket/3-server/test.lol @@ -0,0 +1,11 @@ +HAI 1.4 + CAN HAS SOCKS? + I HAS A sock + I HAS A conn + sock R I IZ SOCKS'Z BIND YR "127.0.0.1" AN YR 13337 MKAY + conn R I IZ SOCKS'Z LISTN YR sock MKAY + I HAS A cmd + cmd R I IZ SOCKS'Z GET YR sock AN YR conn AN YR 10 MKAY + VISIBLE "CMD IZ " AN cmd + BTW $ cat "HAI" | nc 127.0.0.1 13337 +KTHXBYE