448 lines
11 KiB
C
448 lines
11 KiB
C
/* $Id$ */
|
|
/* Copyright (c) 2010 Pierre Pronchery <khorben@defora.org> */
|
|
/* This file is part of DeforaOS System libSystem */
|
|
/* 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, version 3 of the License.
|
|
*
|
|
* 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/>. */
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
#ifdef WITH_SSL
|
|
# include <openssl/ssl.h>
|
|
# include <openssl/err.h>
|
|
#endif
|
|
#include "System.h"
|
|
#include "appinterface.h"
|
|
|
|
|
|
/* AppClient */
|
|
/* private */
|
|
/* types */
|
|
struct _AppClient
|
|
{
|
|
AppInterface * interface;
|
|
Event * event;
|
|
int fd;
|
|
#define ASC_BUFSIZE 65536 /* FIXME */
|
|
char buf_read[ASC_BUFSIZE];
|
|
size_t buf_read_cnt;
|
|
char buf_write[ASC_BUFSIZE];
|
|
size_t buf_write_cnt;
|
|
char const * lastfunc;
|
|
void ** lastargs;
|
|
int32_t * lastret;
|
|
#ifdef WITH_SSL
|
|
SSL_CTX * ssl_ctx;
|
|
SSL * ssl;
|
|
#endif
|
|
};
|
|
|
|
|
|
/* private */
|
|
/* macros */
|
|
#ifdef WITH_SSL
|
|
# define READ(fd, ac, len) SSL_read(ac->ssl, &ac->buf_read[ac->buf_read_cnt], \
|
|
len)
|
|
# define WRITE(fd, ac, len) SSL_write(ac->ssl, ac->buf_write, len)
|
|
#else
|
|
# define READ(fd, ac, len) read(fd, &ac->buf_read[ac->buf_read_cnt], len)
|
|
# define WRITE(fd, ac, len) write(fd, ac->buf_write, len)
|
|
#endif
|
|
|
|
|
|
/* prototypes */
|
|
static int _appclient_timeout(AppClient * appclient);
|
|
static int _appclient_read(int fd, AppClient * ac);
|
|
static int _appclient_write(int fd, AppClient * ac);
|
|
|
|
#ifdef WITH_SSL
|
|
static char * _appclient_error_ssl(void);
|
|
#endif
|
|
|
|
|
|
/* functions */
|
|
/* appclient_timeout */
|
|
static int _appclient_timeout(AppClient * appclient)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s%d%s", "DEBUG: timeout(", appclient->fd, ")\n");
|
|
#endif
|
|
event_unregister_io_read(appclient->event, appclient->fd);
|
|
event_unregister_io_write(appclient->event, appclient->fd);
|
|
errno = ETIMEDOUT;
|
|
return error_set_code(1, "%s", strerror(errno));
|
|
}
|
|
|
|
|
|
/* appclient_read */
|
|
static int _read_error(AppClient * ac);
|
|
static int _read_unregister(AppClient * appclient);
|
|
|
|
static int _appclient_read(int fd, AppClient * ac)
|
|
{
|
|
ssize_t len = sizeof(ac->buf_read) - ac->buf_read_cnt;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(%d, %p)\n", __func__, fd, (void*)ac);
|
|
#endif
|
|
assert(len >= 0);
|
|
if((len = READ(fd, ac, len)) <= 0)
|
|
return _read_error(ac);
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s%d%s%zd%s", "DEBUG: READ(", fd, ") => ", len, "\n");
|
|
#endif
|
|
ac->buf_read_cnt += len;
|
|
len = appinterface_call_receive(ac->interface, ac->lastret,
|
|
ac->buf_read, ac->buf_read_cnt, ac->lastfunc,
|
|
ac->lastargs);
|
|
if(len < 0)
|
|
{
|
|
#ifdef WITH_SSL
|
|
SSL_shutdown(ac->ssl);
|
|
#endif
|
|
close(ac->fd);
|
|
ac->fd = -1;
|
|
return _read_unregister(ac);
|
|
}
|
|
else if(len == 0) /* FIXME the buffer may actually be too small */
|
|
return 0;
|
|
assert((size_t)len <= ac->buf_read_cnt);
|
|
ac->buf_read_cnt -= len;
|
|
return _read_unregister(ac);
|
|
}
|
|
|
|
static int _read_error(AppClient * ac)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(%p)\n", __func__, (void*)ac);
|
|
#endif
|
|
#ifdef WITH_SSL
|
|
error_set_code(1, "%s", _appclient_error_ssl());
|
|
SSL_shutdown(ac->ssl);
|
|
#else
|
|
error_set_code(1, "%s", strerror(errno));
|
|
#endif
|
|
#ifdef DEBUG
|
|
error_print("DEBUG");
|
|
#endif
|
|
close(ac->fd);
|
|
ac->fd = -1;
|
|
return _read_unregister(ac);
|
|
}
|
|
|
|
static int _read_unregister(AppClient * appclient)
|
|
{
|
|
event_unregister_timeout(appclient->event,
|
|
(EventTimeoutFunc)_appclient_timeout);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* appclient_write */
|
|
static int _write_error(AppClient * ac);
|
|
|
|
static int _appclient_write(int fd, AppClient * ac)
|
|
{
|
|
ssize_t len;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(%d, %p)\n", __func__, fd, (void*)ac);
|
|
#endif
|
|
/* FIXME is EOF an error? */
|
|
if((len = WRITE(fd, ac, ac->buf_write_cnt)) <= 0)
|
|
return _write_error(ac);
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s%d%s%zu%s%zd%s", "DEBUG: WRITE(", fd, ", ",
|
|
ac->buf_write_cnt, ") ", len, "\n");
|
|
#endif
|
|
memmove(ac->buf_write, &ac->buf_write[len], len);
|
|
ac->buf_write_cnt -= len;
|
|
if(ac->buf_write_cnt > 0)
|
|
return 0; /* there is more to write */
|
|
/* read the answer */
|
|
event_register_io_read(ac->event, fd,
|
|
(EventIOFunc)_appclient_read, ac);
|
|
return 1;
|
|
}
|
|
|
|
static int _write_error(AppClient * ac)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(%p)\n", __func__, (void*)ac);
|
|
#endif
|
|
#ifdef WITH_SSL
|
|
error_set_code(1, "%s", _appclient_error_ssl());
|
|
SSL_shutdown(ac->ssl);
|
|
#else
|
|
error_set_code(1, "%s", strerror(errno));
|
|
#endif
|
|
#ifdef DEBUG
|
|
error_print("DEBUG");
|
|
#endif
|
|
close(ac->fd);
|
|
ac->fd = -1;
|
|
return 1;
|
|
}
|
|
|
|
|
|
#ifdef WITH_SSL
|
|
/* appclient_error_ssl */
|
|
static char * _appclient_error_ssl(void)
|
|
{
|
|
return ERR_error_string(ERR_get_error(), NULL);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* public */
|
|
/* functions */
|
|
/* appclient_new */
|
|
AppClient * appclient_new(char const * app)
|
|
{
|
|
AppClient * appclient;
|
|
Event * event;
|
|
|
|
if((event = event_new()) == NULL)
|
|
return NULL;
|
|
if((appclient = appclient_new_event(app, event)) == NULL)
|
|
{
|
|
event_delete(event);
|
|
return NULL;
|
|
}
|
|
return appclient;
|
|
}
|
|
|
|
|
|
/* appclient_new_event */
|
|
static int _new_connect(AppClient * appclient, char const * app);
|
|
static int _connect_addr(String const * service, uint32_t * addr);
|
|
|
|
AppClient * appclient_new_event(char const * app, Event * event)
|
|
{
|
|
AppClient * appclient;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s%s%s%s", __func__, "(\"", app, "\")\n");
|
|
#endif
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(\"%s\", %p)\n", __func__, app, (void*)event);
|
|
#endif
|
|
if((appclient = object_new(sizeof(AppClient))) == NULL)
|
|
return NULL;
|
|
if((appclient->interface = appinterface_new("Init")) == NULL)
|
|
{
|
|
object_delete(appclient);
|
|
return NULL;
|
|
}
|
|
appclient->event = event;
|
|
appclient->buf_read_cnt = 0;
|
|
appclient->buf_write_cnt = 0;
|
|
#ifdef WITH_SSL
|
|
appclient->ssl = NULL;
|
|
if((appclient->ssl_ctx = SSL_CTX_new(SSLv3_client_method())) == NULL
|
|
|| SSL_CTX_set_cipher_list(appclient->ssl_ctx,
|
|
SSL_DEFAULT_CIPHER_LIST) != 1
|
|
|| _new_connect(appclient, app) != 0)
|
|
#else
|
|
if(_new_connect(appclient, app) != 0)
|
|
#endif
|
|
{
|
|
appclient_delete(appclient);
|
|
appclient = NULL;
|
|
}
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(\"%s\", %p) => %p\n", __func__, app,
|
|
(void*)event, (void*)appclient);
|
|
#endif
|
|
return appclient;
|
|
}
|
|
|
|
static int _new_connect(AppClient * appclient, char const * app)
|
|
{
|
|
struct sockaddr_in sa;
|
|
int32_t port = -1;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(%p, \"%s\")\n", __func__, (void*)appclient,
|
|
app);
|
|
#endif
|
|
if((appclient->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
|
return 1;
|
|
sa.sin_family = AF_INET;
|
|
sa.sin_port = htons(appinterface_get_port(appclient->interface));
|
|
if(_connect_addr("Init", &sa.sin_addr.s_addr) != 0)
|
|
return 1;
|
|
if(connect(appclient->fd, (struct sockaddr *)&sa, sizeof(sa)) != 0)
|
|
return error_set_code(1, "%s%s", "Init: ", strerror(errno));
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: connect(%d, %s:%d) => 0\n", appclient->fd,
|
|
inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
|
|
#endif
|
|
#ifdef WITH_SSL
|
|
if((appclient->ssl = SSL_new(appclient->ssl_ctx)) == NULL
|
|
|| SSL_set_fd(appclient->ssl, appclient->fd) != 1)
|
|
return error_set_code(1, "%s", _appclient_error_ssl());
|
|
SSL_set_connect_state(appclient->ssl);
|
|
#endif
|
|
if(appclient_call(appclient, &port, "get_session", app) != 0)
|
|
return 1;
|
|
if(port < 0)
|
|
return error_set_code(1, "%s", "Could not obtain remote port");
|
|
if(port == 0) /* the connection is good already or being forwarded */
|
|
return 0;
|
|
#ifdef WITH_SSL
|
|
SSL_shutdown(appclient->ssl);
|
|
SSL_free(appclient->ssl);
|
|
appclient->ssl = NULL;
|
|
#endif
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s%d%s", "DEBUG: Bouncing to port ", port, "\n");
|
|
#endif
|
|
appinterface_delete(appclient->interface);
|
|
if((appclient->interface = appinterface_new(app)) == NULL)
|
|
return 1;
|
|
close(appclient->fd);
|
|
if((appclient->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
|
return error_set_code(1, "%s%s", "socket: ", strerror(errno));
|
|
if(_connect_addr(app, &sa.sin_addr.s_addr) != 0)
|
|
return 1;
|
|
sa.sin_port = htons(port);
|
|
if(connect(appclient->fd, (struct sockaddr *)&sa, sizeof(sa)) != 0)
|
|
return error_set_code(1, "%s%s%s", app, ": ", strerror(errno));
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: connect(%d, %s:%d) => 0\n", appclient->fd,
|
|
inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
|
|
#endif
|
|
#ifdef WITH_SSL
|
|
if((appclient->ssl = SSL_new(appclient->ssl_ctx)) == NULL
|
|
|| SSL_set_fd(appclient->ssl, appclient->fd) != 1)
|
|
return error_set_code(1, "%s", _appclient_error_ssl());
|
|
SSL_set_connect_state(appclient->ssl);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int _connect_addr(String const * service, uint32_t * addr)
|
|
{
|
|
char prefix[] = "APPSERVER_";
|
|
size_t len;
|
|
char * env;
|
|
char const * server;
|
|
struct hostent * he;
|
|
|
|
len = sizeof(prefix) + string_length(service) + 1;
|
|
if((env = malloc(len)) == NULL)
|
|
return error_set_code(1, "%s", strerror(errno));
|
|
snprintf(env, len, "%s%s", prefix, service);
|
|
server = getenv(env);
|
|
free(env);
|
|
if(server == NULL)
|
|
{
|
|
*addr = htonl(INADDR_LOOPBACK);
|
|
return 0;
|
|
}
|
|
if((he = gethostbyname(server)) == NULL)
|
|
return error_set_code(1, "%s", hstrerror(h_errno));
|
|
*addr = *((uint32_t*)he->h_addr);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* appclient_delete */
|
|
void appclient_delete(AppClient * appclient)
|
|
{
|
|
appinterface_delete(appclient->interface);
|
|
if(appclient->fd != -1)
|
|
close(appclient->fd);
|
|
#ifdef WITH_SSL
|
|
if(appclient->ssl != NULL)
|
|
SSL_free(appclient->ssl);
|
|
if(appclient->ssl_ctx != NULL)
|
|
SSL_CTX_free(appclient->ssl_ctx);
|
|
#endif
|
|
object_delete(appclient);
|
|
}
|
|
|
|
|
|
/* useful */
|
|
/* appclient_call */
|
|
static int _call_event(AppClient * ac);
|
|
|
|
int appclient_call(AppClient * ac, int32_t * ret, char const * function, ...)
|
|
{
|
|
void ** args = NULL;
|
|
va_list arg;
|
|
size_t left;
|
|
size_t cnt;
|
|
ssize_t i;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(%p, %p, \"%s\", ...)\n", __func__, (void*)ac,
|
|
ret, function);
|
|
#endif
|
|
if(appinterface_get_args_count(ac->interface, &cnt, function) != 0)
|
|
return 1;
|
|
if((args = calloc(sizeof(*args), cnt)) == NULL)
|
|
return error_set_code(1, "%s", strerror(errno));
|
|
assert(sizeof(ac->buf_write) >= ac->buf_write_cnt);
|
|
left = sizeof(ac->buf_write) - ac->buf_write_cnt;
|
|
va_start(arg, function);
|
|
i = appinterface_call(ac->interface, &ac->buf_write[ac->buf_write_cnt],
|
|
left, function, args, arg);
|
|
va_end(arg);
|
|
if(i <= 0)
|
|
{
|
|
free(args);
|
|
return 1;
|
|
}
|
|
assert((size_t)i <= left);
|
|
ac->lastfunc = function; /* XXX safe for now because synchronous only */
|
|
ac->lastargs = args;
|
|
ac->lastret = ret;
|
|
ac->buf_write_cnt += i;
|
|
i = _call_event(ac);
|
|
free(args);
|
|
return (i == 0) ? 0 : 1;
|
|
}
|
|
|
|
static int _call_event(AppClient * ac)
|
|
/* FIXME don't block processing of other events */
|
|
{
|
|
Event * eventtmp;
|
|
struct timeval tv = { 10, 0 };
|
|
|
|
eventtmp = ac->event;
|
|
ac->event = event_new();
|
|
event_register_timeout(ac->event, &tv,
|
|
(EventTimeoutFunc)_appclient_timeout, ac);
|
|
event_register_io_write(ac->event, ac->fd,
|
|
(EventIOFunc)_appclient_write, ac);
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s() %s\n", __func__, "Waiting for answer");
|
|
#endif
|
|
event_loop(ac->event);
|
|
event_delete(ac->event); /* FIXME may already be free'd */
|
|
ac->event = eventtmp;
|
|
return (ac->fd >= 0) ? 0 : 1;
|
|
}
|