blob: cf9191ceb0e723dd6fa592299222451edd2b8676 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2005 The Regents of the University of California.
* This material was produced under U.S. Government contract W-7405-ENG-36
* for Los Alamos National Laboratory, which is operated by the University
* of California for the U.S. Department of Energy. The U.S. Government has
* rights to use, reproduce, and distribute this software. NEITHER THE
* GOVERNMENT NOR THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR
* ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified
* to produce derivative works, such modified software should be clearly
* marked, so as not to confuse it with the version available from LANL.
*
* Additionally, this program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* LA-CC 04-115
******************************************************************************/
/*
* The proxy handles communication between the client debug library API and the
* client debugger, since they may be running on different hosts, and will
* certainly be running in different processes.
*/
#ifdef __gnu_linux__
#define _GNU_SOURCE
#endif /* __gnu_linux__ */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdlib.h>
#include "compat.h"
#include "proxy.h"
#include "proxy_event.h"
#include "proxy_tcp.h"
#include "handler.h"
struct timeval SELECT_TIMEOUT = {0, 1000};
static int proxy_tcp_clnt_init(proxy_clnt *, void **, char *, va_list);
static int proxy_tcp_clnt_connect(proxy_clnt *);
static int proxy_tcp_clnt_create(proxy_clnt *);
static int proxy_tcp_clnt_accept(int, void *);
static int proxy_tcp_clnt_progress(proxy_clnt *);
static void proxy_tcp_clnt_event_callback(void *, void *);
static void proxy_tcp_clnt_cmd_callback(void *, void *);
proxy_clnt_funcs proxy_tcp_clnt_funcs =
{
proxy_tcp_clnt_init,
proxy_tcp_clnt_connect,
proxy_tcp_clnt_create,
proxy_tcp_clnt_progress,
};
static int
proxy_tcp_clnt_recv_msgs(int fd, void *data)
{
proxy_clnt * clnt = (proxy_clnt *)data;
proxy_tcp_conn * conn = (proxy_tcp_conn *)clnt->clnt_data;
return proxy_tcp_recv_msgs(conn);
}
/*
* CLIENT FUNCTIONS
*/
static int
proxy_tcp_clnt_init(proxy_clnt *pc, void **data, char *attr, va_list ap)
{
int port;
char * host = NULL;
proxy_tcp_conn * conn;
while (attr != NULL) {
if (strcmp(attr, "host") == 0)
host = strdup(va_arg(ap, char *));
else if (strcmp(attr, "port") == 0)
port = va_arg(ap, int);
attr = va_arg(ap, char *);
}
proxy_tcp_create_conn(&conn);
conn->clnt = pc;
if (host != NULL)
conn->host = strdup(host);
conn->port = port;
*data = (void *)conn;
return PROXY_RES_OK;
}
/**
* Connect to a remote proxy.
*/
static int
proxy_tcp_clnt_connect(proxy_clnt *pc)
{
SOCKET sd;
struct hostent * hp;
long int haddr;
struct sockaddr_in scket;
proxy_tcp_conn * conn = (proxy_tcp_conn *)pc->clnt_data;
if (conn->host == NULL) {
proxy_set_error(PROXY_ERR_CLIENT, "no host specified");
return PROXY_RES_ERR;
}
hp = gethostbyname(conn->host);
if (hp == (struct hostent *)NULL) {
proxy_set_error(PROXY_ERR_CLIENT, "could not find host");
return PROXY_RES_ERR;
}
haddr = ((hp->h_addr[0] & 0xff) << 24) |
((hp->h_addr[1] & 0xff) << 16) |
((hp->h_addr[2] & 0xff) << 8) |
((hp->h_addr[3] & 0xff) << 0);
if ( (sd = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET )
{
proxy_set_error(PROXY_ERR_SYSTEM, strerror(errno));
return PROXY_RES_ERR;
}
memset (&scket,0,sizeof(scket));
scket.sin_family = PF_INET;
scket.sin_port = htons((u_short) conn->port);
scket.sin_addr.s_addr = htonl(haddr);
if ( connect(sd, (struct sockaddr *) &scket, sizeof(scket)) == SOCKET_ERROR )
{
proxy_set_error(PROXY_ERR_SYSTEM, strerror(errno));
CLOSE_SOCKET(sd);
return PROXY_RES_ERR;
}
conn->sess_sock = sd;
conn->connected++;
RegisterEventHandler(PROXY_EVENT_HANDLER, proxy_tcp_clnt_event_callback, (void *)pc);
RegisterEventHandler(PROXY_CMD_HANDLER, proxy_tcp_clnt_cmd_callback, (void *)pc);
RegisterFileHandler(sd, READ_FILE_HANDLER, proxy_tcp_clnt_recv_msgs, (void *)pc);
return PROXY_RES_OK;
}
static int
proxy_tcp_clnt_create(proxy_clnt *pc)
{
socklen_t slen;
SOCKET sd;
struct sockaddr_in sname;
proxy_tcp_conn * conn = (proxy_tcp_conn *)pc->clnt_data;
if ( (sd = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET )
{
proxy_set_error(PROXY_ERR_SYSTEM, strerror(errno));
return PROXY_RES_ERR;
}
memset (&sname, 0, sizeof(sname));
sname.sin_family = PF_INET;
sname.sin_port = htons(conn->port);
sname.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sd,(struct sockaddr *) &sname, sizeof(sname)) == SOCKET_ERROR )
{
proxy_set_error(PROXY_ERR_SYSTEM, strerror(errno));
CLOSE_SOCKET(sd);
return PROXY_RES_ERR;
}
slen = sizeof(sname);
if ( getsockname(sd, (struct sockaddr *)&sname, &slen) == SOCKET_ERROR )
{
proxy_set_error(PROXY_ERR_SYSTEM, strerror(errno));
CLOSE_SOCKET(sd);
return PROXY_RES_ERR;
}
if ( listen(sd, 5) == SOCKET_ERROR )
{
proxy_set_error(PROXY_ERR_SYSTEM, strerror(errno));
CLOSE_SOCKET(sd);
return PROXY_RES_ERR;
}
conn->svr_sock = sd;
conn->port = (int) ntohs(sname.sin_port);
RegisterFileHandler(sd, READ_FILE_HANDLER, proxy_tcp_clnt_accept, (void *)pc);
RegisterEventHandler(PROXY_EVENT_HANDLER, proxy_tcp_clnt_event_callback, (void *)pc);
RegisterEventHandler(PROXY_CMD_HANDLER, proxy_tcp_clnt_cmd_callback, (void *)pc);
return PROXY_RES_OK;
}
static int
proxy_tcp_clnt_accept(int fd, void *data)
{
SOCKET ns;
socklen_t fromlen;
struct sockaddr addr;
proxy_clnt * pc = (proxy_clnt *)data;
proxy_tcp_conn * conn = (proxy_tcp_conn *)pc->clnt_data;
fromlen = sizeof(addr);
ns = accept(fd, &addr, &fromlen);
if (ns < 0) {
proxy_set_error(PROXY_ERR_SYSTEM, strerror(errno));
return PROXY_RES_ERR;
}
/*
* Only allow one connection at a time.
*/
if (conn->sess_sock != INVALID_SOCKET) {
CLOSE_SOCKET(ns); // reject
return PROXY_RES_OK;
}
conn->sess_sock = ns;
conn->connected++;
RegisterFileHandler(ns, READ_FILE_HANDLER, proxy_tcp_clnt_recv_msgs, (void *)pc);
if (pc->clnt_helper_funcs->eventhandler != NULL) {
proxy_msg *m = new_proxy_msg(PROXY_EV_CONNECTED, 0); // TODO trans id should NOT be 0
proxy_queue_msg(pc->clnt_events, m);
pc->clnt_helper_funcs->eventhandler(m, pc->clnt_helper_funcs->eventdata);
free_proxy_msg(m);
}
return PROXY_RES_OK;
}
static void
proxy_tcp_clnt_process_cmds()
{
CallEventHandlers(PROXY_CMD_HANDLER, NULL);
}
static void
proxy_tcp_clnt_process_events(proxy_msg *msg, void *data)
{
CallEventHandlers(PROXY_EVENT_HANDLER, (void *)msg);
}
static int
proxy_tcp_clnt_progress(proxy_clnt *clnt)
{
fd_set rfds;
fd_set wfds;
fd_set efds;
int res;
int nfds = 0;
struct timeval tv;
struct timeval * timeout = &tv;
proxy_process_msgs(clnt->clnt_events, proxy_tcp_clnt_process_events, NULL);
/* Set up fd sets */
GenerateFDSets(&nfds, &rfds, &wfds, &efds);
if (clnt->clnt_timeout == NULL) {
timeout = NULL;
} else {
memcpy((char *)timeout, (char *)clnt->clnt_timeout, sizeof(struct timeval));
}
for ( ;; ) {
res = select(nfds+1, &rfds, &wfds, &efds, &tv);
switch (res) {
case INVALID_SOCKET:
if ( errno == EINTR )
continue;
perror("socket");
return PROXY_RES_ERR;
case 0:
/* Timeout. */
break;
default:
if (CallFileHandlers(&rfds, &wfds, &efds) < 0)
return PROXY_RES_ERR;
}
break;
}
proxy_tcp_clnt_process_cmds();
return PROXY_RES_OK;
}
/*
* Reads events from proxy peer and dispatches them.
*/
static void
proxy_tcp_clnt_event_callback(void *ev_data, void *data)
{
int len;
char * result;
proxy_msg * m;
proxy_clnt * clnt = (proxy_clnt *)ev_data;
proxy_tcp_conn * conn = (proxy_tcp_conn *)clnt->clnt_data;
if (proxy_tcp_get_msg(conn, &result, &len) <= 0 ||
clnt->clnt_helper_funcs->eventhandler == NULL)
return;
if (proxy_deserialize_msg(result, len, &m) < 0) {
m = new_proxy_msg(PROXY_EV_MESSAGE, 0); // TODO trans id should NOT be 0
proxy_msg_add_int(m, 3); /* 3 attributes */
proxy_msg_add_keyval_string(m, MSG_LEVEL_ATTR, MSG_LEVEL_FATAL);
proxy_msg_add_keyval_int(m, MSG_CODE_ATTR, PROXY_ERR_PROTO);
proxy_msg_add_keyval_string(m, MSG_TEXT_ATTR, "Could not covert to event");
}
free(result);
clnt->clnt_helper_funcs->eventhandler(m, clnt->clnt_helper_funcs->eventdata);
}
/*
* Called to process any commands waiting to be sent. The command
* is sent to the proxy peer.
*/
static void
proxy_tcp_clnt_cmd_callback(void *cmd_data, void *data)
{
int len;
char * str;
proxy_clnt * clnt = (proxy_clnt *)cmd_data;
proxy_tcp_conn * conn = (proxy_tcp_conn *)clnt->clnt_data;
proxy_msg * msg = (proxy_msg *)data;
if (proxy_serialize_msg(msg, &str, &len) < 0) {
/*
* TODO should send an error back to proxy peer
*/
fprintf(stderr, "proxy_tcp_svr_event_callback: event conversion failed\n");
return;
}
(void)proxy_tcp_send_msg(conn, str, len);
free(str);
}