-- cgit From 93b557bf0a56e24cd4fb3f1452c90e3e6c253e97 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 16:55:21 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@32 f02b47b9-160a-0410-81a6-dc3441afb0ec --- Makefile | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 66ec1b8..7b62a04 100644 --- a/Makefile +++ b/Makefile @@ -3,13 +3,16 @@ OSARCH=$(shell uname -s) OSREV=$(shell uname -r) -VERSION := 1.13 -DESTDIR := +VERSION := 1.20pre +DESTDIR ?= CONFDIR:=/etc/asterisk CONFDIR_REAL := $(DESTDIR)/etc/asterisk +PERMDIR:=/etc/asterisk +PERMDIR_REAL := $(DESTDIR)/etc/asterisk LIBDIR := $(DESTDIR)/usr/lib/astmanproxy CONFFILE := astmanproxy.conf +PERMFILE := astmanproxy.users DISTDIR := /var/www/html/astmanproxy @@ -20,18 +23,19 @@ PREFIX:= /usr/local BINDIR := $(DESTDIR)$(PREFIX)/sbin # For compilation dependencies -MODS := astmanproxy config common proxyfunc log +MODS := astmanproxy config config_perms common proxyfunc log HANDLERS := xml standard csv http SOBJS := $(HANDLERS:%=%.so) ifeq (${OSARCH},Darwin) LIBS=-lresolv CFLAGS+=-D__Darwin_ -# -DFINK_BUILD BINDIR=/opt/sbin LIBDIR=/opt/lib/astmanproxy CONFDIR=/opt/etc/asterisk CONFDIR_REAL=/opt/etc/asterisk + PERMDIR=/opt/etc/asterisk + PERMDIR_REAL=/opt/etc/asterisk LOGDIR=/opt/log/asterisk OBJS+=dlfcn.o poll.o ASTLINK=-Wl,-force_flat_namespace,-dynamic @@ -47,11 +51,13 @@ endif OBJS += $(MODS:%=%.o) MODDIR := $(LIBDIR)/modules CONF_TARGET:= $(CONFDIR_REAL)/$(CONFFILE) -DEFINES:='-DPROXY_VERSION="$(VERSION)"' '-DCDIR="$(CONFDIR)"' '-DCFILE="$(CONFFILE)"' '-DMDIR="$(MODDIR)"' +PERM_TARGET:= $(PERMDIR_REAL)/$(PERMFILE) +DEFINES:='-DPROXY_VERSION="$(VERSION)"' '-DCDIR="$(CONFDIR)"' '-DCFILE="$(CONFFILE)"' '-DMDIR="$(MODDIR)"' '-DPDIR="$(PERMDIR)"' '-DPFILE="$(PERMFILE)"' +VPATH = src # Add -g below for debug/GDB symbols #CFLAGS+= $(DEFINES) -Wall -O2 -dynamic -D_REENTRANT -fPIC -CFLAGS+= $(DEFINES) -Wall -O2 -D_REENTRANT -fPIC +CFLAGS+= $(DEFINES) -Wall -O2 -D_REENTRANT -fPIC -Isrc/include # For printing only SRCS := $(MODS:%=%.c) @@ -78,6 +84,9 @@ install: uninstall all if [ ! -f $(CONF_TARGET) ]; then \ install $(CONFFILE) $(CONF_TARGET); \ fi + if [ ! -f $(PERM_TARGET) ]; then \ + install $(PERMFILE) $(PERM_TARGET); \ + fi @echo "Installation Complete!" uninstall: -- cgit From ad2c8750819f7e236c0baaf7edf2affd50c9d0a0 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 16:56:25 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@34 f02b47b9-160a-0410-81a6-dc3441afb0ec --- astmanproxy.c | 664 ----------------------- astmanproxy.h | 113 ---- astmanproxy.users | 2 + common.c | 147 ----- config.c | 280 ---------- csv.c | 33 -- dlfcn-compat.h | 83 --- dlfcn.c | 1282 -------------------------------------------- http.c | 137 ----- log.c | 57 -- poll-compat.h | 101 ---- poll.c | 306 ----------- proxyfunc.c | 258 --------- src/astmanproxy.c | 676 +++++++++++++++++++++++ src/common.c | 147 +++++ src/config.c | 280 ++++++++++ src/config_perms.c | 127 +++++ src/csv.c | 33 ++ src/dlfcn.c | 1282 ++++++++++++++++++++++++++++++++++++++++++++ src/http.c | 137 +++++ src/include/astmanproxy.h | 125 +++++ src/include/dlfcn-compat.h | 83 +++ src/include/poll-compat.h | 101 ++++ src/log.c | 57 ++ src/poll.c | 306 +++++++++++ src/proxyfunc.c | 336 ++++++++++++ src/standard.c | 79 +++ src/xml.c | 163 ++++++ standard.c | 75 --- xml.c | 163 ------ 30 files changed, 3934 insertions(+), 3699 deletions(-) delete mode 100644 astmanproxy.c delete mode 100644 astmanproxy.h create mode 100644 astmanproxy.users delete mode 100644 common.c delete mode 100644 config.c delete mode 100644 csv.c delete mode 100644 dlfcn-compat.h delete mode 100644 dlfcn.c delete mode 100644 http.c delete mode 100644 log.c delete mode 100644 poll-compat.h delete mode 100644 poll.c delete mode 100644 proxyfunc.c create mode 100644 src/astmanproxy.c create mode 100644 src/common.c create mode 100644 src/config.c create mode 100644 src/config_perms.c create mode 100644 src/csv.c create mode 100644 src/dlfcn.c create mode 100644 src/http.c create mode 100644 src/include/astmanproxy.h create mode 100644 src/include/dlfcn-compat.h create mode 100644 src/include/poll-compat.h create mode 100644 src/log.c create mode 100644 src/poll.c create mode 100644 src/proxyfunc.c create mode 100644 src/standard.c create mode 100644 src/xml.c delete mode 100644 standard.c delete mode 100644 xml.c diff --git a/astmanproxy.c b/astmanproxy.c deleted file mode 100644 index c0a8470..0000000 --- a/astmanproxy.c +++ /dev/null @@ -1,664 +0,0 @@ -/* Asterisk Manager Proxy - Copyright (c) 2005 David C. Troy - - This program is free software, distributed under the terms of - the GNU General Public License. - -*/ - -#include "astmanproxy.h" - -extern int LoadHandlers( void ); -extern void ReadConfig( void ); -extern FILE *OpenLogfile( void ); -extern int SetProcUID( void ); - -extern void *proxyaction_do(char *proxyaction, struct message *m, struct mansession *s); -extern void *ProxyLogin(struct mansession *s); -extern void *ProxyLogoff(struct mansession *s); - -int ConnectAsterisk(struct mansession *s); - -struct proxyconfig pc; -struct mansession *sessions = NULL; -struct iohandler *iohandlers = NULL; - -pthread_mutex_t sessionlock; -pthread_mutex_t serverlock; -pthread_mutex_t loglock; -pthread_mutex_t debuglock; -static int asock = -1; -FILE *proxylog; -int debug = 0; - -void hup(int sig) { - if (proxylog) { - fflush(proxylog); - fclose(proxylog); - } - proxylog = OpenLogfile(); - logmsg("Received HUP -- reopened log"); -} - -void leave(int sig) { - struct mansession *c; - struct message sm, cm; - struct iohandler *io; - struct ast_server *srv; - char iabuf[INET_ADDRSTRLEN]; - - /* Message to send to servers */ - memset(&sm, 0, sizeof(struct message)); - AddHeader(&sm, "Action: Logoff"); - - /* Message to send to clients */ - memset(&cm, 0, sizeof(struct message)); - AddHeader(&cm, PROXY_SHUTDOWN); - - if (debug) - debugmsg("Notifying and closing sessions"); - pthread_mutex_lock (&sessionlock); - while (sessions) { - c = sessions; - sessions = sessions->next; - - if (c->server) { - if (debug) - debugmsg("asterisk@%s: closing session", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); - c->output->write(c, &sm); - logmsg("Shutdown, closed server %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); - } else { - if (debug) - debugmsg("client@%s: closing session", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); - c->output->write(c, &cm); - logmsg("Shutdown, closed client %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); - } - close(c->fd); - pthread_mutex_destroy(&c->lock); - free(c); - } - pthread_mutex_unlock (&sessionlock); - - /* unload server list */ - while (pc.serverlist) { - srv = pc.serverlist; - pc.serverlist = srv->next; - if (debug) - debugmsg("asterisk@%s: forgetting", srv->ast_host); - free(srv); - } - - if (debug) - debugmsg("Closing listener socket"); - close(asock); - - /* unload io handlers */ - while (iohandlers) { - io = iohandlers; - iohandlers = iohandlers->next; - if (debug) - debugmsg("unloading: %s", io->formatname); - dlclose(io->dlhandle); - free(io); - } - - if(debug) - debugmsg("Done!\n"); - logmsg("Proxy stopped; shutting down."); - - fclose(proxylog); - pthread_mutex_destroy(&sessionlock); - pthread_mutex_destroy(&loglock); - pthread_mutex_destroy(&debuglock); - exit(sig); -} - -void Version( void ) -{ - printf("astmanproxy: Version %s, (C) David C. Troy\n", PROXY_VERSION); - return; -} - -void Usage( void ) -{ - printf("Usage: astmanproxy [-d|-h|-v]\n"); - printf(" -d : Start in Debug Mode\n"); - printf(" -h : Displays this message\n"); - printf(" -v : Displays version information\n"); - printf("Start with no options to run as daemon\n"); - return; -} - -void destroy_session(struct mansession *s) -{ - struct mansession *cur, *prev = NULL; - char iabuf[INET_ADDRSTRLEN]; - - pthread_mutex_lock(&sessionlock); - cur = sessions; - while(cur) { - if (cur == s) - break; - prev = cur; - cur = cur->next; - } - if (cur) { - if (prev) - prev->next = cur->next; - else - sessions = cur->next; - debugmsg("Connection closed: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); - if (s->fd > -1) - close(s->fd); - pthread_mutex_destroy(&s->lock); - free(s); - } else - debugmsg("Trying to delete non-existent session %p?\n", s); - pthread_mutex_unlock(&sessionlock); - - /* If there are no servers and no clients, why are we here? */ - if (!sessions) { - logmsg("Cannot connect to any servers! Leaving!"); - leave(0); - } -} - -int WriteClients(struct message *m) { - struct mansession *c; - char *actionid; - - c = sessions; - while (c) { - if ( !c->server && m->hdrcount>1 ) { - if (c->autofilter && c->actionid) { - actionid = astman_get_header(m, "ActionID"); - if ( !strcmp(actionid, c->actionid) ) { - c->output->write(c, m); - } - } else - c->output->write(c, m); - if ( c->input->autodisconnect && c->input->autodisconnect() ) - close(c->fd); - } - c = c->next; - } - return 1; -} - -int WriteAsterisk(struct message *m) { - int i; - char outstring[MAX_LEN], *dest; - struct mansession *s, *first; - - first = NULL; - dest = NULL; - - s = sessions; - - dest = astman_get_header(m, "Server"); - if (debug && *dest) debugmsg("set destination: %s", dest); - while ( s ) { - if ( s->server && (s->connected > 0) ) { - if ( !first ) - first = s; - if (*dest && !strcasecmp(dest, s->server->ast_host) ) - break; - } - s = s->next; - } - - if (!s) - s = first; - - /* Check for no servers and empty block -- Don't pester Asterisk if it is one*/ - if (!s || !s->server || (!m->hdrcount && !m->headers[0][0]) ) - return 1; - - debugmsg("writing block to %s", s->server->ast_host); - - pthread_mutex_lock(&s->lock); - for (i=0; ihdrcount; i++) { - if (strcasecmp(m->headers[i], "Server:") ) { - sprintf(outstring, "%s\r\n", m->headers[i]); - write(s->fd, outstring, strlen(outstring) ); - } - } - write(s->fd, "\r\n", 2); - pthread_mutex_unlock(&s->lock); - return 1; -} - -void *setactionid(char *actionid, struct message *m, struct mansession *s) -{ - pthread_mutex_lock(&s->lock); - strncpy(s->actionid, actionid, MAX_LEN); - pthread_mutex_unlock(&s->lock); - - return 0; -} - -/* Handles proxy client sessions; closely based on session_do from asterisk's manager.c */ -void *session_do(struct mansession *s) -{ - struct message m; - int res; - char *proxyaction, *actionid, *action, *key; - - if (s->input->onconnect) - s->input->onconnect(s, &m); - - for (;;) { - /* Get a complete message block from input handler */ - memset(&m, 0, sizeof(struct message) ); - res = s->input->read(s, &m); - m.session = s; - - if (res > 0) { - /* Check for anything that requires proxy-side processing */ - if (pc.key && !s->authenticated) { - key = astman_get_header(&m, "ProxyKey"); - if (!strcmp(key, pc.key) ) { - pthread_mutex_lock(&s->lock); - s->authenticated = 1; - pthread_mutex_unlock(&s->lock); - } else - break; - } - - proxyaction = astman_get_header(&m, "ProxyAction"); - actionid = astman_get_header(&m, "ActionID"); - action = astman_get_header(&m, "Action"); - if ( !strcasecmp(action, "Login") ) - ProxyLogin(s); - else if ( !strcasecmp(action, "Logoff") ) - ProxyLogoff(s); - else if ( !(*proxyaction == '\0') ) - proxyaction_do(proxyaction, &m, s); - else { - if ( !(*actionid == '\0') ) - setactionid(actionid, &m, s); - if ( !WriteAsterisk(&m) ) - break; - } - } else if (res < 0) - break; - } - - destroy_session(s); - if (debug) - debugmsg("Exiting session_do thread"); - pthread_exit(NULL); - return NULL; -} - -void *HandleAsterisk(struct mansession *s) -{ - struct message m; - int res,i; - char iabuf[INET_ADDRSTRLEN]; - - if (ConnectAsterisk(s)) - goto leave; - for (;;) { - - debugmsg("asterisk@%s: attempting read...", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); - memset(&m, 0, sizeof(struct message) ); - res = s->input->read(s, &m); - m.session = s; - - if (res > 0) { - if (debug) { - for(i=0; isin.sin_addr), m.headers[i]); - } - } - - if (!s->connected) { - if ( !strcmp("Authentication accepted", astman_get_header(&m, "Message")) ) { - s->connected = 1; - if (debug) - debugmsg("asterisk@%s: connected successfully!", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr) ); - } - if ( !strcmp("Authentication failed", astman_get_header(&m, "Message")) ) { - s->connected = -1; - } - } - - m.session = s; - AddHeader(&m, "Server: %s", m.session->server->ast_host); - - if (!WriteClients(&m)) - break; - /* TODO: does it make any sense to abort * conn if we cannot write to clients? I don't think so. - Do we even get a return code back that means anything? I don't think so. */ - } else if (res < 0) { - if (debug) - debugmsg("asterisk@%s: Not connected", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); - if ( ConnectAsterisk(s) ) - break; - } - } - -leave: - if (debug) - debugmsg("asterisk@%s: Giving up and exiting thread", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr) ); - destroy_session(s); - pthread_exit(NULL); - return NULL; -} - -int ConnectAsterisk(struct mansession *s) { - char iabuf[INET_ADDRSTRLEN]; - int r = 1, res = 0; - struct message m; - - /* Don't try to do this if auth has already failed! */ - if (s->connected < 0 ) - return 1; - else - s->connected = 0; - - if (debug) - debugmsg("asterisk@%s: Connecting (u=%s, p=%s)", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), - s->server->ast_user, s->server->ast_pass); - - /* Construct auth message just once */ - memset( &m, 0, sizeof(struct message) ); - AddHeader(&m, "Action: Login"); - AddHeader(&m, "Username: %s", s->server->ast_user); - AddHeader(&m, "Secret: %s", s->server->ast_pass); - AddHeader(&m, "Events: %s", s->server->ast_events); - - for ( ;; ) { - if ( connect_nonb(s->fd, (struct sockaddr *) &s->sin, sizeof(s->sin), 2) < 0 ) { - if (errno == EISCONN) { - pthread_mutex_lock(&s->lock); - s->fd = socket(AF_INET, SOCK_STREAM, 0); - pthread_mutex_unlock(&s->lock); - } - if (debug) - debugmsg("asterisk@%s: Connect failed, Retrying (%d) %s", - ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), r, strerror(errno) ); - if (pc.maxretries && (++r>pc.maxretries) ) { - res = 1; - break; - } else - sleep(pc.retryinterval); - } else { - /* Send login */ - s->output->write(s, &m); - res = 0; - break; - } - } - - return res; -} - -int StartServer(struct ast_server *srv) { - - struct mansession *s; - struct hostent *ast_hostent; - - char iabuf[INET_ADDRSTRLEN]; - pthread_attr_t attr; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - ast_hostent = gethostbyname(srv->ast_host); - if (!ast_hostent) { - logmsg("Cannot resolve host %s, cannot add!", srv->ast_host); - debugmsg("Cannot resolve host %s, cannot add!", srv->ast_host); - return 1; - } - - s = malloc(sizeof(struct mansession)); - if ( !s ) { - logmsg("Failed to allocate server session: %s\n", strerror(errno)); - debugmsg("Failed to allocate server session: %s\n", strerror(errno)); - return 1; - } - - memset(s, 0, sizeof(struct mansession)); - SetIOHandlers(s, "standard", "standard"); - s->server = srv; - - bzero((char *) &s->sin,sizeof(s->sin)); - s->sin.sin_family = AF_INET; - memcpy( &s->sin.sin_addr.s_addr, ast_hostent->h_addr, ast_hostent->h_length ); - s->sin.sin_port = htons(atoi(s->server->ast_port)); - s->fd = socket(AF_INET, SOCK_STREAM, 0); - - pthread_mutex_lock(&sessionlock); - s->next = sessions; - sessions = s; - pthread_mutex_unlock(&sessionlock); - - logmsg("Allocated Asterisk server session for %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); - if (debug) { - debugmsg("asterisk@%s: Allocated server session", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); - debugmsg("Set %s input format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->input->formatname); - debugmsg("Set %s output format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->output->formatname); - } - - - if (pthread_create(&s->t, &attr, (void *)HandleAsterisk, s)) - destroy_session(s); - else - debugmsg("launched ast %s thread!", s->server->ast_host); - - pthread_attr_destroy(&attr); - return 0; -} - -int LaunchAsteriskThreads() { - - struct ast_server *srv; - - srv = pc.serverlist; - while (srv) { - StartServer(srv); - srv = srv->next; - } - return 0; -} - -int SetIOHandlers(struct mansession *s, char *ifmt, char *ofmt) -{ - int res = 0; - struct iohandler *io; - - io = iohandlers; - pthread_mutex_lock(&s->lock); - while (io) { - if ( !strcasecmp(io->formatname, ifmt) ) - s->input = io; - - if ( !strcasecmp(io->formatname, ofmt) ) - s->output = io; - - io = io->next; - } - - if (!s->output) { - /* TODO: Output debug that default/first handler was used */ - s->output = iohandlers; - res = 1; - } - - if (!s->input) { - /* TODO: Output debug that default/first handler was used */ - s->input = iohandlers; - res = 1; - } - pthread_mutex_unlock(&s->lock); - - return res; -} - -static void *accept_thread() -{ - int as; - struct sockaddr_in sin; - int sinlen; - struct mansession *s; - struct protoent *p; - int arg = 1; - int flags; - pthread_attr_t attr; - char iabuf[INET_ADDRSTRLEN]; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - for (;;) { - sinlen = sizeof(sin); - as = accept(asock, (struct sockaddr *)&sin, &sinlen); - if (as < 0) { - logmsg("Accept returned -1: %s\n", strerror(errno)); - continue; - } - p = (struct protoent *)getprotobyname("tcp"); - if( p ) { - if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) { - logmsg("Failed to set listener tcp connection to TCP_NODELAY mode: %s\n", strerror(errno)); - } - } - s = malloc(sizeof(struct mansession)); - if ( !s ) { - logmsg("Failed to allocate listener session: %s\n", strerror(errno)); - continue; - } - memset(s, 0, sizeof(struct mansession)); - memcpy(&s->sin, &sin, sizeof(sin)); - - /* For safety, make sure socket is non-blocking */ - flags = fcntl(as, F_GETFL); - fcntl(as, F_SETFL, flags | O_NONBLOCK); - - pthread_mutex_init(&s->lock, NULL); - s->fd = as; - SetIOHandlers(s, pc.inputformat, pc.outputformat); - s->autofilter = pc.autofilter; - s->inputcomplete = 0; - s->server = NULL; - - pthread_mutex_lock(&sessionlock); - s->next = sessions; - sessions = s; - pthread_mutex_unlock(&sessionlock); - - logmsg("Connection received from %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); - if (debug) { - debugmsg("Connection received from %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); - debugmsg("Set %s input format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->input->formatname); - debugmsg("Set %s output format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->output->formatname); - } - - if (pthread_create(&s->t, &attr, (void *)session_do, s)) - destroy_session(s); - } - pthread_attr_destroy(&attr); - return NULL; -} - -int main(int argc, char *argv[]) -{ - struct sockaddr_in serv_sock_addr, client_sock_addr; - int cli_addrlen; - struct linger lingerstruct; /* for socket reuse */ - int flag; /* for socket reuse */ - pid_t pid; - char i; - - /* Figure out if we are in debug mode, handle other switches */ - while (( i = getopt( argc, argv, "dhv" ) ) != EOF ) - { - switch( i ) { - case 'd': - debug = 1; - break; - case 'h': - Usage(); - exit(0); - case 'v': - Version(); - exit(0); - case '?': - Usage(); - exit(1); - } - } - - - ReadConfig(); - proxylog = OpenLogfile(); - LoadHandlers(); - - if (SetProcUID()) { - fprintf(stderr,"Cannot set user/group! Check proc_user and proc_group config setting!\n"); - exit(1); - } - - /* If we are not in debug mode, then fork to background */ - if (!debug) { - if ( (pid = fork()) < 0) - exit( 1 ); - else if ( pid > 0) - exit( 0 ); - } - - /* Setup signal handlers */ - (void) signal(SIGINT,leave); - (void) signal(SIGHUP,hup); - (void) signal(SIGTERM,leave); - (void) signal(SIGPIPE, SIG_IGN); - - /* Initialize global mutexes */ - pthread_mutex_init(&sessionlock, NULL); - pthread_mutex_init(&loglock, NULL); - pthread_mutex_init(&debuglock, NULL); - - /* Initialize global client/server list */ - sessions = NULL; - LaunchAsteriskThreads(); - - /* Setup listener socket to setup new sessions... */ - if ((asock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - fprintf(stderr,"Cannot create listener socket!\n"); - exit(1); - } - bzero((char *) &serv_sock_addr, sizeof serv_sock_addr ); - serv_sock_addr.sin_family = AF_INET; - - if ( !strcmp(pc.listen_addr,"*") ) - serv_sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); - else - serv_sock_addr.sin_addr.s_addr = inet_addr( pc.listen_addr); - serv_sock_addr.sin_port = htons((short)pc.listen_port); - - /* Set listener socket re-use options */ - setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag)); - lingerstruct.l_onoff = 1; - lingerstruct.l_linger = 5; - setsockopt(asock, SOL_SOCKET, SO_LINGER, (void *)&lingerstruct, sizeof(lingerstruct)); - - if (bind(asock, (struct sockaddr *) &serv_sock_addr, sizeof serv_sock_addr ) < 0) { - fprintf(stderr,"Cannot bind to listener socket!\n"); - exit(1); - } - - listen(asock, 5); - cli_addrlen = sizeof(client_sock_addr); - if (debug) - debugmsg("Listening for connections"); - logmsg("Proxy Started: Listening for connections"); - - /* Launch listener thread */ - accept_thread(); - - pthread_exit(NULL); - exit(0); -} - diff --git a/astmanproxy.h b/astmanproxy.h deleted file mode 100644 index 566ae69..0000000 --- a/astmanproxy.h +++ /dev/null @@ -1,113 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __APPLE__ - #include "dlfcn-compat.h" - #include "poll-compat.h" -#else - #include - #include -#endif - -#define BUFSIZE 150 -#define MAX_HEADERS 256 -#define MAX_LEN 150 - -#define PROXY_BANNER "Asterisk Call Manager Proxy" -#define PROXY_SHUTDOWN "ProxyMessage: Proxy Shutting Down" - -struct ast_server { - char nickname[80]; - char ast_host[40]; - char ast_port[10]; - char ast_user[80]; - char ast_pass[80]; - char ast_events[10]; - int status; /* TODO: have this mean something */ - struct ast_server *next; -}; - -struct proxyconfig { - struct ast_server *serverlist; - char listen_addr[INET_ADDRSTRLEN]; - int listen_port; - char inputformat[80]; - char outputformat[80]; - int autofilter; - char key[80]; - char proc_user[30]; - char proc_group[30]; - char logfile[80]; - int retryinterval; - int maxretries; -}; - -struct iohandler { - int (*read) (); - int (*write) (); - int (*onconnect) (); - int *(*autodisconnect)(void); - char formatname[80]; - void *dlhandle; - struct iohandler *next; -}; - -struct mansession { - pthread_t t; - pthread_mutex_t lock; - struct sockaddr_in sin; - int fd; - char inbuf[MAX_LEN]; - int inlen; - struct iohandler *input; - struct iohandler *output; - int autofilter; - int inputcomplete; - int authenticated; - int connected; - struct ast_server *server; - char actionid[MAX_LEN]; - struct mansession *next; -}; - -struct message { - int hdrcount; - char headers[MAX_HEADERS][MAX_LEN]; - int in_command; - struct mansession *session; -}; - -struct proxyconfig pc; -extern int debug; - -/* Common Function Prototypes */ -void debugmsg (const char *, ...); -const char *ast_inet_ntoa(char *buf, int bufsiz, struct in_addr ia); -int AddHeader(struct message *m, const char *fmt, ...); -void debugmsg (const char *fmt, ...); -void logmsg (const char *fmt, ...); - -int StartServer(struct ast_server *srv); -int WriteAsterisk(struct message *m); -char *astman_get_header(struct message *m, char *var); -int proxyerror_do(struct mansession *s, char *err); -int get_input(struct mansession *s, char *output); -int SetIOHandlers(struct mansession *s, char *ifmt, char *ofmt); -void destroy_session(struct mansession *s); -int connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec); diff --git a/astmanproxy.users b/astmanproxy.users new file mode 100644 index 0000000..2645fc9 --- /dev/null +++ b/astmanproxy.users @@ -0,0 +1,2 @@ +; user=secret,channel,out_context (to Asterisk),in_context (From Asterisk) +steve=steve,SIP/snom190,local, diff --git a/common.c b/common.c deleted file mode 100644 index 5f63c15..0000000 --- a/common.c +++ /dev/null @@ -1,147 +0,0 @@ -#include "astmanproxy.h" - -/* This routine based on get_input from Asterisk manager.c */ -/* Good generic line-based input routine for \r\n\r\n terminated input */ -/* Used by standard.c and other input handlers */ -int get_input(struct mansession *s, char *output) -{ - /* output must have at least sizeof(s->inbuf) space */ - int res; - int x; - struct pollfd fds[1]; - char iabuf[INET_ADDRSTRLEN]; - - debugmsg("in get_input"); - - /* Look for \r\n from the front, our preferred end of line */ - for (x=0;xinlen;x++) { - int xtra = 0; - if (s->inbuf[x] == '\n') { - if (x && s->inbuf[x-1] == '\r') { - xtra = 1; - } - /* Copy output data not including \r\n */ - memcpy(output, s->inbuf, x - xtra); - /* Add trailing \0 */ - output[x-xtra] = '\0'; - /* Move remaining data back to the front */ - memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x); - s->inlen -= (x + 1); - return 1; - } - } - - if (s->inlen >= sizeof(s->inbuf) - 1) { - if (debug) - debugmsg("Warning: Got long line with no end from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf); - s->inlen = 0; - } - debugmsg("attempting poll operation"); - fds[0].fd = s->fd; - fds[0].events = POLLIN; - res = poll(fds, 1, -1); - debugmsg("returned from poll op"); - if (res < 0 && debug) { - debugmsg("Select returned error"); - } else if (res > 0) { - pthread_mutex_lock(&s->lock); - debugmsg("attempting socket read in get_input..."); - res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen); - pthread_mutex_unlock(&s->lock); - if (res < 1) - return -1; - } - s->inlen += res; - s->inbuf[s->inlen] = '\0'; - return 0; - /* We have some input, but it's not ready for processing */ -} - -char *astman_get_header(struct message *m, char *var) -{ - char cmp[80]; - int x; - snprintf(cmp, sizeof(cmp), "%s: ", var); - for (x=0;xhdrcount;x++) - if (!strncasecmp(cmp, m->headers[x], strlen(cmp))) - return m->headers[x] + strlen(cmp); - return ""; -} - -int AddHeader(struct message *m, const char *fmt, ...) { - va_list ap; - - int res; - - if (m->hdrcount < MAX_HEADERS - 1) { - va_start(ap, fmt); - vsprintf(m->headers[m->hdrcount], fmt, ap); - va_end(ap); - m->hdrcount++; - res = 0; - } else - res = 1; - - return res; -} - -/* Recursive thread safe replacement of inet_ntoa */ -const char *ast_inet_ntoa(char *buf, int bufsiz, struct in_addr ia) -{ - return inet_ntop(AF_INET, &ia, buf, bufsiz); -} - - -int connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec) -{ - int flags, n, error; - socklen_t len; - fd_set rset, wset; - struct timeval tval; - - flags = fcntl(sockfd, F_GETFL, 0); - fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); - - error = 0; - if ( (n = connect(sockfd, (struct sockaddr *) saptr, salen)) < 0) - if (errno != EINPROGRESS) - return(-1); - - /* Do whatever we want while the connect is taking place. */ - - if (n == 0) - goto done; /* connect completed immediately */ - - FD_ZERO(&rset); - FD_SET(sockfd, &rset); - wset = rset; - tval.tv_sec = nsec; - tval.tv_usec = 0; - - if ( (n = select(sockfd+1, &rset, &wset, NULL, - nsec ? &tval : NULL)) == 0) { - /*close(sockfd);*/ /* we want to retry */ - errno = ETIMEDOUT; - return(-1); - } - - if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { - len = sizeof(error); - if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) - return(-1); /* Solaris pending error */ - } else { - /*err_quit("select error: sockfd not set");*/ - logmsg("select error: sockfd not set"); - return(-1); - } - -done: - fcntl(sockfd, F_SETFL, flags); /* restore file status flags */ - - if (error) { - /* close(sockfd); */ /* disable for now, we want to retry... */ - errno = error; - return(-1); - } - return(0); -} diff --git a/config.c b/config.c deleted file mode 100644 index f9d3b9f..0000000 --- a/config.c +++ /dev/null @@ -1,280 +0,0 @@ -#include -#include -#include "astmanproxy.h" - -extern struct iohandler *iohandlers; - -void *add_server(char *srvspec) { - - int ccount = 0; - struct ast_server *srv; - char *s; - - /* malloc ourselves a server credentials structure */ - srv = malloc(sizeof(struct ast_server)); - if ( !srv ) { - fprintf(stderr, "Failed to allocate server credentials: %s\n", strerror(errno)); - exit(1); - } - memset(srv, 0, sizeof (struct ast_server) ); - - s = srvspec; - do { - *s = tolower(*s); - if ( *s == ',' ) { - ccount++; - continue; - } - switch(ccount) { - case 0: - strncat(srv->ast_host, s, 1); - break; - case 1: - strncat(srv->ast_port, s, 1); - break; - case 2: - strncat(srv->ast_user, s, 1); - break; - case 3: - strncat(srv->ast_pass, s, 1); - break; - case 4: - strncat(srv->ast_events, s, 1); - break; - } - } while (*(s++)); - - if (!srv->ast_host || !srv->ast_port || !srv->ast_user || !srv->ast_pass || !srv->ast_events) { - fprintf(stderr, "Aborting! Server spec incomplete: %s!\n", srvspec); - free(srv); - exit(1); - } else { - srv->next = pc.serverlist; - pc.serverlist = srv; - } - - return 0; - /* TODO: make sure server credentials are freed at leave */ -} - -void *processline(char *s) { - char name[80],value[80]; - int nvstate = 0; - - - memset (name,0,sizeof name); - memset (value,0,sizeof value); - - do { - *s = tolower(*s); - - if ( *s == ' ' || *s == '\t') - continue; - if ( *s == ';' || *s == '#' || *s == '\r' || *s == '\n' ) - break; - if ( *s == '=' ) { - nvstate = 1; - continue; - } - if (!nvstate) - strncat(name, s, 1); - else - strncat(value, s, 1); - } while (*(s++)); - - if (debug) - debugmsg("config: %s, %s", name, value); - - if ( !strcmp(name,"host") ) - add_server(value); - else if (!strcmp(name,"retryinterval") ) - pc.retryinterval = atoi(value); - else if (!strcmp(name,"maxretries") ) - pc.maxretries = atoi(value); - else if (!strcmp(name,"listenaddress") ) - strcpy(pc.listen_addr, value); - else if (!strcmp(name,"listenport") ) - pc.listen_port = atoi(value); - else if (!strcmp(name,"proxykey") ) - strcpy(pc.key, value); - else if (!strcmp(name,"proc_user") ) - strcpy(pc.proc_user, value); - else if (!strcmp(name,"proc_group") ) - strcpy(pc.proc_group, value); - else if (!strcmp(name,"logfile") ) - strcpy(pc.logfile, value); - else if (!strcmp(name,"autofilter") ) - pc.autofilter = strcmp(value,"on") ? 0 : 1; - else if (!strcmp(name,"outputformat") ) - strcpy(pc.outputformat, value); - else if (!strcmp(name,"inputformat") ) - strcpy(pc.inputformat, value); - - return 0; -} - -int LoadHandlers() { - - void *dlhandle = NULL; - const char *error; - char fmt[20], moddir[80] = MDIR, modfile[80]; - DIR *mods; - struct dirent *d; - void *rh, *wh, *och; - struct iohandler *io = NULL; - - mods = opendir(moddir); - if (!mods) - exit(1); - - while((d = readdir(mods))) { - /* Must end in .so to load it. */ - if ( (strlen(d->d_name) > 3) && !strcasecmp(d->d_name + strlen(d->d_name) - 3, ".so") ) { - - memset(fmt, 0, sizeof fmt); - strncpy(fmt, d->d_name, strlen(d->d_name) - 3); - - sprintf(modfile, "%s/%s", moddir, d->d_name); - if (debug) - debugmsg("loading: module %s (%s)", fmt, modfile); - - dlhandle = dlopen (modfile, RTLD_LAZY); - if (!dlhandle) { - fprintf(stderr, "dlopen failed: %s\n", dlerror()); - exit(1); - } - - rh = dlsym(dlhandle, "_read"); - if ((error = dlerror()) != NULL) { - if (debug) - debugmsg("loading: note, %s_read does not exist; ignoring", fmt); - } - - wh = dlsym(dlhandle, "_write"); - if ((error = dlerror()) != NULL) { - if (debug) - debugmsg("loading: note, %s_write does not exist; ignoring", fmt); - } - - och = dlsym(dlhandle, "_onconnect"); - if ((error = dlerror()) != NULL) { - if (debug) - debugmsg("loading: note, %s_onconnect does not exist; ignoring", fmt); - } - - if (rh || wh) { - io = malloc(sizeof(struct iohandler)); - memset(io, 0, sizeof(struct iohandler)); - strcpy(io->formatname, fmt); - if (rh) - io->read = rh; - if (wh) - io->write = wh; - if (och) - io->onconnect = och; - io->autodisconnect = dlsym(dlhandle, "_autodisconnect"); - if ((error = dlerror()) != NULL) { - if (debug) - debugmsg("loading: note, %s_autodisconnect not defined; ignoring", fmt); - } - io->dlhandle = dlhandle; - io->next = iohandlers; - iohandlers = io; - } else - dlclose(dlhandle); - - } - } - closedir(mods); - - if (!iohandlers) { - fprintf(stderr, "Unable to load *ANY* IO Handlers from %s!\n", MDIR); - exit(1); - } - - return 0; -} - - -int ReadConfig() { - FILE *FP; - char buf[1024]; - char cfn[80]; - - - memset( &pc, 0, sizeof pc ); - sprintf(cfn, "%s/%s", CDIR, CFILE); - FP = fopen( cfn, "r" ); - - if ( !FP ) - { - fprintf(stderr, "Unable to open config file: %s/%s!\n", CDIR, CFILE); - exit( 1 ); - } - - if (debug) - debugmsg("config: parsing configuration file: %s", cfn); - - while ( fgets( buf, sizeof buf, FP ) ) { - if (*buf == ';' || *buf == '\r' || *buf == '\n' || *buf == '#') continue; - processline(buf); - } - - fclose(FP); - - return 0; -} - -FILE *OpenLogfile() { - FILE *FP; - FP = fopen( pc.logfile, "a" ); - if ( !FP ) { - fprintf(stderr, "Unable to open logfile: %s!", pc.logfile); - exit( 1 ); - } - - return FP; -} - -int SetProcUID() { - - struct passwd *pwent; - struct group *gp; - uid_t newuid = 0; - gid_t newgid = 0; - - if ((pwent = (struct passwd *)getpwnam( pc.proc_user )) == NULL) { - fprintf(stderr, "getpwnam(%s) failed.\n", pc.proc_user); - return(-1); - } else - newuid = pwent->pw_uid; - - if ( newuid == 0 ) { - fprintf(stderr, "getpwnam(%s) returned root user; aborting!\n", pc.proc_user); - return(-1); - } - - if ((gp = (struct group *)getgrnam( pc.proc_group )) == NULL) { - fprintf(stderr, "getgrnam(%s) failed.\n", pc.proc_group); - return(-1); - } else - newgid = gp->gr_gid; - - if ( chown( pc.logfile, newuid, newgid ) < 0 ) - { - fprintf(stderr, "chown(%d,%d) of %s failed!\n", newuid, newgid, pc.logfile); - return( -1 ); - } - - if (setgid(newgid) < 0) { - fprintf(stderr, "setgid(%d) failed.\n", newgid); - return(-1); - } - - if (setuid(newuid) < 0) { - fprintf(stderr, "setuid(%d) failed.\n", newuid); - return(-1); - } - - return 0; -} diff --git a/csv.c b/csv.c deleted file mode 100644 index bf8e800..0000000 --- a/csv.c +++ /dev/null @@ -1,33 +0,0 @@ -/* Asterisk Manager Proxy - Copyright (c) 2005 David C. Troy - - This program is free software, distributed under the terms of - the GNU General Public License. - - CSV I/O Handler -*/ - -#include "astmanproxy.h" - -/* TODO: catch and expand/handle commas in output */ - -int _write(struct mansession *s, struct message *m) { - int i; - char outstring[MAX_LEN]; - - pthread_mutex_lock(&s->lock); - for (i=0; ihdrcount; i++) { - sprintf(outstring, "\"%s\"", m->headers[i]); - if (ihdrcount-1) - strcat(outstring, ", "); - write(s->fd, outstring, strlen(outstring)); - } - write(s->fd, "\r\n\r\n", 4); - pthread_mutex_unlock(&s->lock); - - return 0; -} - -int _autodisconnect() { - return 0; -} diff --git a/dlfcn-compat.h b/dlfcn-compat.h deleted file mode 100644 index 7c5e87f..0000000 --- a/dlfcn-compat.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright (c) 2002 Jorge Acereda & - Peter O'Gorman - -Portions may be copyright others, see the AUTHORS file included with this -distribution. - -Maintained by Peter O'Gorman - -Bug Reports and other queries should go to - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ -#ifndef _DLFCN_H_ -#define _DLFCN_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined (__GNUC__) && __GNUC__ > 3 -#define dl_restrict __restrict -#else -#define dl_restrict -#endif - -#ifndef _POSIX_SOURCE -/* - * Structure filled in by dladdr(). - */ -typedef struct dl_info { - const char *dli_fname; /* Pathname of shared object */ - void *dli_fbase; /* Base address of shared object */ - const char *dli_sname; /* Name of nearest symbol */ - void *dli_saddr; /* Address of nearest symbol */ -} Dl_info; - -extern int dladdr(const void * dl_restrict, Dl_info * dl_restrict); -#endif /* ! _POSIX_SOURCE */ - -extern int dlclose(void * handle); -extern char * dlerror(void); -extern void * dlopen(const char *path, int mode); -extern void * dlsym(void * dl_restrict handle, const char * dl_restrict symbol); - -#define RTLD_LAZY 0x1 -#define RTLD_NOW 0x2 -#define RTLD_LOCAL 0x4 -#define RTLD_GLOBAL 0x8 - -#ifndef _POSIX_SOURCE -#define RTLD_NOLOAD 0x10 -#define RTLD_NODELETE 0x80 - -/* - * Special handle arguments for dlsym(). - */ -#define RTLD_NEXT ((void *) -1) /* Search subsequent objects. */ -#define RTLD_DEFAULT ((void *) -2) /* Use default search algorithm. */ -#endif /* ! _POSIX_SOURCE */ - -#ifdef __cplusplus -} -#endif - -#endif /* _DLFCN_H_ */ diff --git a/dlfcn.c b/dlfcn.c deleted file mode 100644 index 98d2373..0000000 --- a/dlfcn.c +++ /dev/null @@ -1,1282 +0,0 @@ -/* -Copyright (c) 2002 Jorge Acereda & - Peter O'Gorman - -Portions may be copyright others, see the AUTHORS file included with this -distribution. - -Maintained by Peter O'Gorman - -Bug Reports and other queries should go to - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -/* Just playing to see if it would compile with the freebsd headers, it does, - * but because of the different values for RTLD_LOCAL etc, it would break binary - * compat... oh well - */ -#ifndef __BSD_VISIBLE -#define __BSD_VISIBLE 1 -#endif -#include "dlfcn-compat.h" - -#ifndef dl_restrict -#define dl_restrict __restrict -#endif -/* This is not available on 10.1 */ -#ifndef LC_LOAD_WEAK_DYLIB -#define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD) -#endif - -/* With this stuff here, this thing may actually compile/run on 10.0 systems - * Not that I have a 10.0 system to test it on anylonger - */ -#ifndef LC_REQ_DYLD -#define LC_REQ_DYLD 0x80000000 -#endif -#ifndef NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED -#define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4 -#endif -#ifndef NSADDIMAGE_OPTION_RETURN_ON_ERROR -#define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 -#endif -#ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_BIND -#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0 -#endif -#ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR -#define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4 -#endif -/* These symbols will be looked for in dyld */ -static const struct mach_header *(*dyld_NSAddImage) (const char *, unsigned long) = 0; -static int (*dyld_NSIsSymbolNameDefinedInImage) (const struct mach_header *, const char *) = 0; -static NSSymbol(*dyld_NSLookupSymbolInImage) - (const struct mach_header *, const char *, unsigned long) = 0; - -/* Define this to make dlcompat reuse data block. This way in theory we save - * a little bit of overhead. However we then couldn't correctly catch excess - * calls to dlclose(). Hence we don't use this feature - */ -#undef REUSE_STATUS - -/* Size of the internal error message buffer (used by dlerror()) */ -#define ERR_STR_LEN 251 - -/* Maximum number of search paths supported by getSearchPath */ -#define MAX_SEARCH_PATHS 32 - - -#define MAGIC_DYLIB_OFI ((NSObjectFileImage) 'DYOF') -#define MAGIC_DYLIB_MOD ((NSModule) 'DYMO') - -/* internal flags */ -#define DL_IN_LIST 0x01 - -/* our mutex */ -static pthread_mutex_t dlcompat_mutex; -/* Our thread specific storage - */ -static pthread_key_t dlerror_key; - -struct dlthread -{ - int lockcnt; - unsigned char errset; - char errstr[ERR_STR_LEN]; -}; - -/* This is our central data structure. Whenever a module is loaded via - * dlopen(), we create such a struct. - */ -struct dlstatus -{ - struct dlstatus *next; /* pointer to next element in the linked list */ - NSModule module; - const struct mach_header *lib; - int refs; /* reference count */ - int mode; /* mode in which this module was loaded */ - dev_t device; - ino_t inode; - int flags; /* Any internal flags we may need */ -}; - -/* Head node of the dlstatus list */ -static struct dlstatus mainStatus = { 0, MAGIC_DYLIB_MOD, NULL, -1, RTLD_GLOBAL, 0, 0, 0 }; -static struct dlstatus *stqueue = &mainStatus; - - -/* Storage for the last error message (used by dlerror()) */ -/* static char err_str[ERR_STR_LEN]; */ -/* static int err_filled = 0; */ - -/* Prototypes to internal functions */ -static void debug(const char *fmt, ...); -static void error(const char *str, ...); -static const char *safegetenv(const char *s); -static const char *searchList(void); -static const char *getSearchPath(int i); -static const char *getFullPath(int i, const char *file); -static const struct stat *findFile(const char *file, const char **fullPath); -static int isValidStatus(struct dlstatus *status); -static inline int isFlagSet(int mode, int flag); -static struct dlstatus *lookupStatus(const struct stat *sbuf); -static void insertStatus(struct dlstatus *dls, const struct stat *sbuf); -static int promoteLocalToGlobal(struct dlstatus *dls); -static void *reference(struct dlstatus *dls, int mode); -static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError); -static struct dlstatus *allocStatus(void); -static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode); -static NSSymbol *search_linked_libs(const struct mach_header *mh, const char *symbol); -static const char *get_lib_name(const struct mach_header *mh); -static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod); -static void dlcompat_init_func(void); -static inline void dlcompat_init_check(void); -static inline void dolock(void); -static inline void dounlock(void); -static void dlerrorfree(void *data); -static void resetdlerror(void); -static const struct mach_header *my_find_image(const char *name); -static const struct mach_header *image_for_address(const void *address); -static inline const char *dyld_error_str(void); - -#if FINK_BUILD -/* Two Global Functions */ -void *dlsym_prepend_underscore(void *handle, const char *symbol); -void *dlsym_auto_underscore(void *handle, const char *symbol); - -/* And their _intern counterparts */ -static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol); -static void *dlsym_auto_underscore_intern(void *handle, const char *symbol); -#endif - -/* Functions */ - -static void debug(const char *fmt, ...) -{ -#if DEBUG > 1 - va_list arg; - va_start(arg, fmt); - fprintf(stderr, "DLDEBUG: "); - vfprintf(stderr, fmt, arg); - fprintf(stderr, "\n"); - fflush(stderr); - va_end(arg); -#endif -} - -static void error(const char *str, ...) -{ - va_list arg; - struct dlthread *tss; - char * err_str; - va_start(arg, str); - tss = pthread_getspecific(dlerror_key); - err_str = tss->errstr; - strncpy(err_str, "dlcompat: ", ERR_STR_LEN); - vsnprintf(err_str + 10, ERR_STR_LEN - 10, str, arg); - va_end(arg); - debug("ERROR: %s\n", err_str); - tss->errset = 1; -} - -static void warning(const char *str) -{ -#if DEBUG > 0 - fprintf(stderr, "WARNING: dlcompat: %s\n", str); -#endif -} - -static const char *safegetenv(const char *s) -{ - const char *ss = getenv(s); - return ss ? ss : ""; -} - -/* because this is only used for debugging and error reporting functions, we - * don't really care about how elegant it is... it could use the load - * commands to find the install name of the library, but... - */ -static const char *get_lib_name(const struct mach_header *mh) -{ - unsigned long count = _dyld_image_count(); - unsigned long i; - const char *val = NULL; - if (mh) - { - for (i = 0; i < count; i++) - { - if (mh == _dyld_get_image_header(i)) - { - val = _dyld_get_image_name(i); - break; - } - } - } - return val; -} - -/* Returns the mach_header for the module bu going through all the loaded images - * and finding the one with the same name as the module. There really ought to be - * an api for doing this, would be faster, but there isn't one right now - */ -static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod) -{ - const char *mod_name = NSNameOfModule(mod); - struct mach_header *mh = NULL; - unsigned long count = _dyld_image_count(); - unsigned long i; - debug("Module name: %s", mod_name); - for (i = 0; i < count; i++) - { - if (!strcmp(mod_name, _dyld_get_image_name(i))) - { - mh = _dyld_get_image_header(i); - break; - } - } - return mh; -} - - -/* Compute and return a list of all directories that we should search when - * trying to locate a module. We first look at the values of LD_LIBRARY_PATH - * and DYLD_LIBRARY_PATH, and then finally fall back to looking into - * /usr/lib and /lib. Since both of the environments variables can contain a - * list of colon seperated paths, we simply concat them and the two other paths - * into one big string, which we then can easily parse. - * Splitting this string into the actual path list is done by getSearchPath() - */ -static const char *searchList() -{ - size_t buf_size; - static char *buf=NULL; - const char *ldlp = safegetenv("LD_LIBRARY_PATH"); - const char *dyldlp = safegetenv("DYLD_LIBRARY_PATH"); - const char *stdpath = getenv("DYLD_FALLBACK_LIBRARY_PATH"); - if (!stdpath) - stdpath = "/usr/local/lib:/lib:/usr/lib"; - if (!buf) - { - buf_size = strlen(ldlp) + strlen(dyldlp) + strlen(stdpath) + 4; - buf = malloc(buf_size); - snprintf(buf, buf_size, "%s%s%s%s%s%c", dyldlp, (dyldlp[0] ? ":" : ""), ldlp, (ldlp[0] ? ":" : ""), - stdpath, '\0'); - } - return buf; -} - -/* Returns the ith search path from the list as computed by searchList() */ -static const char *getSearchPath(int i) -{ - static const char *list = 0; - static char **path = (char **)0; - static int end = 0; - static int numsize = MAX_SEARCH_PATHS; - static char **tmp; - /* So we can call free() in the "destructor" we use i=-1 to return the alloc'd array */ - if (i == -1) - { - return (const char*)path; - } - if (!path) - { - path = (char **)calloc(MAX_SEARCH_PATHS, sizeof(char **)); - } - if (!list && !end) - list = searchList(); - if (i >= (numsize)) - { - debug("Increasing size for long PATH"); - tmp = (char **)calloc((MAX_SEARCH_PATHS + numsize), sizeof(char **)); - if (tmp) - { - memcpy(tmp, path, sizeof(char **) * numsize); - free(path); - path = tmp; - numsize += MAX_SEARCH_PATHS; - } - else - { - return 0; - } - } - - while (!path[i] && !end) - { - path[i] = strsep((char **)&list, ":"); - - if (path[i][0] == 0) - path[i] = 0; - end = (list == 0); - } - return path[i]; -} - -static const char *getFullPath(int i, const char *file) -{ - static char buf[PATH_MAX]; - const char *path = getSearchPath(i); - if (path) - { - snprintf(buf, PATH_MAX, "%s/%s", path, file); - } - return path ? buf : 0; -} - -/* Given a file name, try to determine the full path for that file. Starts - * its search in the current directory, and then tries all paths in the - * search list in the order they are specified there. - */ -static const struct stat *findFile(const char *file, const char **fullPath) -{ - int i = 0; - static struct stat sbuf; - char *fileName; - debug("finding file %s", file); - *fullPath = file; - if (0 == stat(file, &sbuf)) - return &sbuf; - if (strchr(file, '/')) - return 0; /* If the path had a / we don't look in env var places */ - fileName = NULL; - if (!fileName) - fileName = (char *)file; - while ((*fullPath = getFullPath(i++, fileName))) - { - if (0 == stat(*fullPath, &sbuf)) - return &sbuf; - } - ; - return 0; -} - -/* Determine whether a given dlstatus is valid or not */ -static int isValidStatus(struct dlstatus *status) -{ - /* Walk the list to verify status is contained in it */ - struct dlstatus *dls = stqueue; - while (dls && status != dls) - dls = dls->next; - if (dls == 0) - error("invalid handle"); - else if ((dls->module == 0) || (dls->refs == 0)) - error("handle to closed library"); - else - return TRUE; - return FALSE; -} - -static inline int isFlagSet(int mode, int flag) -{ - return (mode & flag) == flag; -} - -static struct dlstatus *lookupStatus(const struct stat *sbuf) -{ - struct dlstatus *dls = stqueue; - debug("looking for status"); - while (dls && ( /* isFlagSet(dls->mode, RTLD_UNSHARED) */ 0 - || sbuf->st_dev != dls->device || sbuf->st_ino != dls->inode)) - dls = dls->next; - return dls; -} - -static void insertStatus(struct dlstatus *dls, const struct stat *sbuf) -{ - debug("inserting status"); - dls->inode = sbuf->st_ino; - dls->device = sbuf->st_dev; - dls->refs = 0; - dls->mode = 0; - if ((dls->flags & DL_IN_LIST) == 0) - { - dls->next = stqueue; - stqueue = dls; - dls->flags |= DL_IN_LIST; - } -} - -static struct dlstatus *allocStatus() -{ - struct dlstatus *dls; -#ifdef REUSE_STATUS - dls = stqueue; - while (dls && dls->module) - dls = dls->next; - if (!dls) -#endif - dls = calloc(sizeof(*dls),1); - return dls; -} - -static int promoteLocalToGlobal(struct dlstatus *dls) -{ - static int (*p) (NSModule module) = 0; - debug("promoting"); - if (!p) - _dyld_func_lookup("__dyld_NSMakePrivateModulePublic", (unsigned long *)&p); - return (dls->module == MAGIC_DYLIB_MOD) || (p && p(dls->module)); -} - -static void *reference(struct dlstatus *dls, int mode) -{ - if (dls) - { - if (dls->module == MAGIC_DYLIB_MOD && isFlagSet(mode, RTLD_LOCAL)) - { - warning("trying to open a .dylib with RTLD_LOCAL"); - error("unable to open a .dylib with RTLD_LOCAL"); - return NULL; - } - if (isFlagSet(mode, RTLD_GLOBAL) && - !isFlagSet(dls->mode, RTLD_GLOBAL) && !promoteLocalToGlobal(dls)) - { - error("unable to promote local module to global"); - return NULL; - } - dls->mode |= mode; - dls->refs++; - } - else - debug("reference called with NULL argument"); - - return dls; -} - -static const struct mach_header *my_find_image(const char *name) -{ - const struct mach_header *mh = 0; - const char *id = NULL; - int i = _dyld_image_count(); - int j; - mh = (struct mach_header *) - dyld_NSAddImage(name, NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED | - NSADDIMAGE_OPTION_RETURN_ON_ERROR); - if (!mh) - { - for (j = 0; j < i; j++) - { - id = _dyld_get_image_name(j); - if (!strcmp(id, name)) - { - mh = _dyld_get_image_header(j); - break; - } - } - } - return mh; -} - -/* - * dyld adds libraries by first adding the directly dependant libraries in link order, and - * then adding the dependencies for those libraries, so we should do the same... but we don't - * bother adding the extra dependencies, if the symbols are neither in the loaded image nor - * any of it's direct dependencies, then it probably isn't there. - */ -NSSymbol *search_linked_libs(const struct mach_header * mh, const char *symbol) -{ - unsigned int n; - struct load_command *lc = 0; - struct mach_header *wh; - NSSymbol *nssym = 0; - if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) - { - lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); - for (n = 0; n < mh->ncmds; n++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) - { - if ((LC_LOAD_DYLIB == lc->cmd) || (LC_LOAD_WEAK_DYLIB == lc->cmd)) - { - if ((wh = (struct mach_header *) - my_find_image((char *)(((struct dylib_command *)lc)->dylib.name.offset + - (char *)lc)))) - { - if (dyld_NSIsSymbolNameDefinedInImage(wh, symbol)) - { - nssym = dyld_NSLookupSymbolInImage(wh, - symbol, - NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | - NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); - break; - } - } - } - } - if ((!nssym) && NSIsSymbolNameDefined(symbol)) - { - /* I've never seen this debug message...*/ - debug("Symbol \"%s\" is defined but was not found", symbol); - } - } - return nssym; -} - -/* Up to the caller to free() returned string */ -static inline const char *dyld_error_str() -{ - NSLinkEditErrors dylder; - int dylderno; - const char *dylderrstr; - const char *dyldfile; - const char* retStr = NULL; - NSLinkEditError(&dylder, &dylderno, &dyldfile, &dylderrstr); - if (dylderrstr && strlen(dylderrstr)) - { - retStr = malloc(strlen(dylderrstr) +1); - strcpy((char*)retStr,dylderrstr); - } - return retStr; -} - -static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError) -{ - NSSymbol *nssym = 0; -#ifdef __GCC__ - void *caller = __builtin_return_address(1); /* Be *very* careful about inlining */ -#else - void *caller = NULL; -#endif - const struct mach_header *caller_mh = 0; - const char* savedErrorStr = NULL; - resetdlerror(); -#ifndef RTLD_SELF -#define RTLD_SELF ((void *) -3) -#endif - if (NULL == dls) - dls = RTLD_SELF; - if ((RTLD_NEXT == dls) || (RTLD_SELF == dls)) - { - if (dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage && caller) - { - caller_mh = image_for_address(caller); - if (RTLD_SELF == dls) - { - /* FIXME: We should be using the NSModule api, if SELF is an MH_BUNDLE - * But it appears to work anyway, and looking at the code in dyld_libfuncs.c - * this is acceptable. - */ - if (dyld_NSIsSymbolNameDefinedInImage(caller_mh, symbol)) - { - nssym = dyld_NSLookupSymbolInImage(caller_mh, - symbol, - NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | - NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); - } - } - if (!nssym) - { - if (RTLD_SELF == dls) - savedErrorStr = dyld_error_str(); - nssym = search_linked_libs(caller_mh, symbol); - } - } - else - { - if (canSetError) - error("RTLD_SELF and RTLD_NEXT are not supported"); - return NULL; - } - } - if (!nssym) - { - - if (RTLD_DEFAULT == dls) - { - dls = &mainStatus; - } - if (!isValidStatus(dls)) - return NULL; - - if (dls->module != MAGIC_DYLIB_MOD) - { - nssym = NSLookupSymbolInModule(dls->module, symbol); - if (!nssym && NSIsSymbolNameDefined(symbol)) - { - debug("Searching dependencies"); - savedErrorStr = dyld_error_str(); - nssym = search_linked_libs(get_mach_header_from_NSModule(dls->module), symbol); - } - } - else if (dls->lib && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) - { - if (dyld_NSIsSymbolNameDefinedInImage(dls->lib, symbol)) - { - nssym = dyld_NSLookupSymbolInImage(dls->lib, - symbol, - NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | - NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); - } - else if (NSIsSymbolNameDefined(symbol)) - { - debug("Searching dependencies"); - savedErrorStr = dyld_error_str(); - nssym = search_linked_libs(dls->lib, symbol); - } - } - else if (dls->module == MAGIC_DYLIB_MOD) - { - /* Global context, use NSLookupAndBindSymbol */ - if (NSIsSymbolNameDefined(symbol)) - { - /* There doesn't seem to be a return on error option for this call??? - this is potentially broken, if binding fails, it will improperly - exit the application. */ - nssym = NSLookupAndBindSymbol(symbol); - } - else - { - if (savedErrorStr) - free((char*)savedErrorStr); - savedErrorStr = malloc(256); - snprintf((char*)savedErrorStr, 256, "Symbol \"%s\" not in global context",symbol); - } - } - } - /* Error reporting */ - if (!nssym) - { - if (!savedErrorStr || !strlen(savedErrorStr)) - { - if (savedErrorStr) - free((char*)savedErrorStr); - savedErrorStr = malloc(256); - snprintf((char*)savedErrorStr, 256,"Symbol \"%s\" not found",symbol); - } - if (canSetError) - { - error(savedErrorStr); - } - else - { - debug(savedErrorStr); - } - if (savedErrorStr) - free((char*)savedErrorStr); - return NULL; - } - return NSAddressOfSymbol(nssym); -} - -static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode) -{ - NSObjectFileImage ofi = 0; - NSObjectFileImageReturnCode ofirc; - struct dlstatus *dls; - NSLinkEditErrors ler; - int lerno; - const char *errstr; - const char *file; - void (*init) (void); - ofirc = NSCreateObjectFileImageFromFile(path, &ofi); - switch (ofirc) - { - case NSObjectFileImageSuccess: - break; - case NSObjectFileImageInappropriateFile: - if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) - { - if (isFlagSet(mode, RTLD_LOCAL)) - { - warning("trying to open a .dylib with RTLD_LOCAL"); - error("unable to open this file with RTLD_LOCAL"); - return NULL; - } - } - else - { - error("opening this file is unsupported on this system"); - return NULL; - } - break; - case NSObjectFileImageFailure: - error("object file setup failure"); - return NULL; - case NSObjectFileImageArch: - error("no object for this architecture"); - return NULL; - case NSObjectFileImageFormat: - error("bad object file format"); - return NULL; - case NSObjectFileImageAccess: - error("can't read object file"); - return NULL; - default: - error("unknown error from NSCreateObjectFileImageFromFile()"); - return NULL; - } - dls = lookupStatus(sbuf); - if (!dls) - { - dls = allocStatus(); - } - if (!dls) - { - error("unable to allocate memory"); - return NULL; - } - // dls->lib = 0; - if (ofirc == NSObjectFileImageInappropriateFile) - { - if ((dls->lib = dyld_NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR))) - { - debug("Dynamic lib loaded at %ld", dls->lib); - ofi = MAGIC_DYLIB_OFI; - dls->module = MAGIC_DYLIB_MOD; - ofirc = NSObjectFileImageSuccess; - /* Although it is possible with a bit of work to modify this so it works and - functions with RTLD_NOW, I don't deem it necessary at the moment */ - } - if (!(dls->module)) - { - NSLinkEditError(&ler, &lerno, &file, &errstr); - if (!errstr || (!strlen(errstr))) - error("Can't open this file type"); - else - error(errstr); - if ((dls->flags & DL_IN_LIST) == 0) - { - free(dls); - } - return NULL; - } - } - else - { - dls->module = NSLinkModule(ofi, path, - NSLINKMODULE_OPTION_RETURN_ON_ERROR | - NSLINKMODULE_OPTION_PRIVATE | - (isFlagSet(mode, RTLD_NOW) ? NSLINKMODULE_OPTION_BINDNOW : 0)); - NSDestroyObjectFileImage(ofi); - if (dls->module) - { - dls->lib = get_mach_header_from_NSModule(dls->module); - } - } - if (!dls->module) - { - NSLinkEditError(&ler, &lerno, &file, &errstr); - if ((dls->flags & DL_IN_LIST) == 0) - { - free(dls); - } - error(errstr); - return NULL; - } - - insertStatus(dls, sbuf); - dls = reference(dls, mode); - if ((init = dlsymIntern(dls, "__init", 0))) - { - debug("calling _init()"); - init(); - } - return dls; -} - -inline static void dlcompat_init_check(void) -{ - static pthread_mutex_t l = PTHREAD_MUTEX_INITIALIZER; - static int init_done = 0; - - pthread_mutex_lock(&l); - if (!init_done) { - dlcompat_init_func(); - init_done = 1; - } - pthread_mutex_unlock(&l); -} - -static void dlcompat_init_func(void) -{ - _dyld_func_lookup("__dyld_NSAddImage", (unsigned long *)&dyld_NSAddImage); - _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage", - (unsigned long *)&dyld_NSIsSymbolNameDefinedInImage); - _dyld_func_lookup("__dyld_NSLookupSymbolInImage", (unsigned long *)&dyld_NSLookupSymbolInImage); - if (pthread_mutex_init(&dlcompat_mutex, NULL)) - exit(1); - if (pthread_key_create(&dlerror_key, &dlerrorfree)) - exit(1); -} - -static void resetdlerror() -{ - struct dlthread *tss; - tss = pthread_getspecific(dlerror_key); - tss->errset = 0; -} - -static void dlerrorfree(void *data) -{ - free(data); -} - -/* We kind of want a recursive lock here, but meet a little trouble - * because they are not available pre OS X 10.2, so we fake it - * using thread specific storage to keep a lock count - */ -static inline void dolock(void) -{ - int err = 0; - struct dlthread *tss; - dlcompat_init_check(); - tss = pthread_getspecific(dlerror_key); - if (!tss) - { - tss = malloc(sizeof(struct dlthread)); - tss->lockcnt = 0; - tss->errset = 0; - if (pthread_setspecific(dlerror_key, tss)) - { - fprintf(stderr,"dlcompat: pthread_setspecific failed\n"); - exit(1); - } - } - if (!tss->lockcnt) - err = pthread_mutex_lock(&dlcompat_mutex); - tss->lockcnt = tss->lockcnt +1; - if (err) - exit(err); -} - -static inline void dounlock(void) -{ - int err = 0; - struct dlthread *tss; - tss = pthread_getspecific(dlerror_key); - tss->lockcnt = tss->lockcnt -1; - if (!tss->lockcnt) - err = pthread_mutex_unlock(&dlcompat_mutex); - if (err) - exit(err); -} - -void *dlopen(const char *path, int mode) -{ - const struct stat *sbuf; - struct dlstatus *dls; - const char *fullPath; - - dolock(); - resetdlerror(); - if (!path) - { - dls = &mainStatus; - goto dlopenok; - } - if (!(sbuf = findFile(path, &fullPath))) - { - error("file \"%s\" not found", path); - goto dlopenerror; - } - /* Now checks that it hasn't been closed already */ - if ((dls = lookupStatus(sbuf)) && (dls->refs > 0)) - { - debug("status found"); - dls = reference(dls, mode); - goto dlopenok; - } -#ifdef RTLD_NOLOAD - if (isFlagSet(mode, RTLD_NOLOAD)) - { - error("no existing handle and RTLD_NOLOAD specified"); - goto dlopenerror; - } -#endif - if (isFlagSet(mode, RTLD_LAZY) && isFlagSet(mode, RTLD_NOW)) - { - error("how can I load something both RTLD_LAZY and RTLD_NOW?"); - goto dlopenerror; - } - dls = loadModule(fullPath, sbuf, mode); - - dlopenok: - dounlock(); - return (void *)dls; - dlopenerror: - dounlock(); - return NULL; -} - -#if !FINK_BUILD -void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol) -{ - int sym_len = strlen(symbol); - void *value = NULL; - char *malloc_sym = NULL; - dolock(); - malloc_sym = malloc(sym_len + 2); - if (malloc_sym) - { - sprintf(malloc_sym, "_%s", symbol); - value = dlsymIntern(handle, malloc_sym, 1); - free(malloc_sym); - } - else - { - error("Unable to allocate memory"); - goto dlsymerror; - } - dounlock(); - return value; - dlsymerror: - dounlock(); - return NULL; -} -#endif - -#if FINK_BUILD - -void *dlsym_prepend_underscore(void *handle, const char *symbol) -{ - void *answer; - dolock(); - answer = dlsym_prepend_underscore_intern(handle, symbol); - dounlock(); - return answer; -} - -static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol) -{ -/* - * A quick and easy way for porting packages which call dlsym(handle,"sym") - * If the porter adds -Ddlsym=dlsym_prepend_underscore to the CFLAGS then - * this function will be called, and will add the required underscore. - * - * Note that I haven't figured out yet which should be "standard", prepend - * the underscore always, or not at all. These global functions need to go away - * for opendarwin. - */ - int sym_len = strlen(symbol); - void *value = NULL; - char *malloc_sym = NULL; - malloc_sym = malloc(sym_len + 2); - if (malloc_sym) - { - sprintf(malloc_sym, "_%s", symbol); - value = dlsymIntern(handle, malloc_sym, 1); - free(malloc_sym); - } - else - { - error("Unable to allocate memory"); - } - return value; -} - -void *dlsym_auto_underscore(void *handle, const char *symbol) -{ - void *answer; - dolock(); - answer = dlsym_auto_underscore_intern(handle, symbol); - dounlock(); - return answer; - -} -static void *dlsym_auto_underscore_intern(void *handle, const char *symbol) -{ - struct dlstatus *dls = handle; - void *addr = 0; - addr = dlsymIntern(dls, symbol, 0); - if (!addr) - addr = dlsym_prepend_underscore_intern(handle, symbol); - return addr; -} - - -void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol) -{ - struct dlstatus *dls = handle; - void *addr = 0; - dolock(); - addr = dlsymIntern(dls, symbol, 1); - dounlock(); - return addr; -} -#endif - -int dlclose(void *handle) -{ - struct dlstatus *dls = handle; - dolock(); - resetdlerror(); - if (!isValidStatus(dls)) - { - goto dlcloseerror; - } - if (dls->module == MAGIC_DYLIB_MOD) - { - const char *name; - if (!dls->lib) - { - name = "global context"; - } - else - { - name = get_lib_name(dls->lib); - } - warning("trying to close a .dylib!"); - error("Not closing \"%s\" - dynamic libraries cannot be closed", name); - goto dlcloseerror; - } - if (!dls->module) - { - error("module already closed"); - goto dlcloseerror; - } - - if (dls->refs == 1) - { - unsigned long options = 0; - void (*fini) (void); - if ((fini = dlsymIntern(dls, "__fini", 0))) - { - debug("calling _fini()"); - fini(); - } - options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES; -#ifdef RTLD_NODELETE - if (isFlagSet(dls->mode, RTLD_NODELETE)) - options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED; -#endif - if (!NSUnLinkModule(dls->module, options)) - { - error("unable to unlink module"); - goto dlcloseerror; - } - dls->refs--; - dls->module = 0; - /* Note: the dlstatus struct dls is neither removed from the list - * nor is the memory it occupies freed. This shouldn't pose a - * problem in mostly all cases, though. - */ - } - dounlock(); - return 0; - dlcloseerror: - dounlock(); - return 1; -} - -char *dlerror(void) -{ - struct dlthread *tss; - const char * err_str = NULL; - dlcompat_init_check(); - tss = pthread_getspecific(dlerror_key); - if (tss != NULL && tss->errset != 0) { - tss->errset = 0; - err_str = tss->errstr; - } - return (err_str); -} - -/* Given an address, return the mach_header for the image containing it - * or zero if the given address is not contained in any loaded images. - */ -const struct mach_header *image_for_address(const void *address) -{ - unsigned long i; - unsigned long j; - unsigned long count = _dyld_image_count(); - struct mach_header *mh = 0; - struct load_command *lc = 0; - unsigned long addr = NULL; - for (i = 0; i < count; i++) - { - addr = (unsigned long)address - _dyld_get_image_vmaddr_slide(i); - mh = _dyld_get_image_header(i); - if (mh) - { - lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); - for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) - { - if (LC_SEGMENT == lc->cmd && - addr >= ((struct segment_command *)lc)->vmaddr && - addr < - ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize) - { - goto image_found; - } - } - } - mh = 0; - } - image_found: - return mh; -} - -int dladdr(const void * dl_restrict p, Dl_info * dl_restrict info) -{ -/* - FIXME: USe the routine image_for_address. -*/ - unsigned long i; - unsigned long j; - unsigned long count = _dyld_image_count(); - struct mach_header *mh = 0; - struct load_command *lc = 0; - unsigned long addr = NULL; - unsigned long table_off = (unsigned long)0; - int found = 0; - if (!info) - return 0; - dolock(); - resetdlerror(); - info->dli_fname = 0; - info->dli_fbase = 0; - info->dli_sname = 0; - info->dli_saddr = 0; -/* Some of this was swiped from code posted by Douglas Davidson - * to darwin-development AT lists DOT apple DOT com and slightly modified - */ - for (i = 0; i < count; i++) - { - addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(i); - mh = _dyld_get_image_header(i); - if (mh) - { - lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); - for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) - { - if (LC_SEGMENT == lc->cmd && - addr >= ((struct segment_command *)lc)->vmaddr && - addr < - ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize) - { - info->dli_fname = _dyld_get_image_name(i); - info->dli_fbase = (void *)mh; - found = 1; - break; - } - } - if (found) - break; - } - } - if (!found) - { - dounlock(); - return 0; - } - lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); - for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) - { - if (LC_SEGMENT == lc->cmd) - { - if (!strcmp(((struct segment_command *)lc)->segname, "__LINKEDIT")) - break; - } - } - table_off = - ((unsigned long)((struct segment_command *)lc)->vmaddr) - - ((unsigned long)((struct segment_command *)lc)->fileoff) + _dyld_get_image_vmaddr_slide(i); - debug("table off %x", table_off); - - lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); - for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) - { - if (LC_SYMTAB == lc->cmd) - { - - struct nlist *symtable = (struct nlist *)(((struct symtab_command *)lc)->symoff + table_off); - unsigned long numsyms = ((struct symtab_command *)lc)->nsyms; - struct nlist *nearest = NULL; - unsigned long diff = 0xffffffff; - unsigned long strtable = (unsigned long)(((struct symtab_command *)lc)->stroff + table_off); - debug("symtable %x", symtable); - for (i = 0; i < numsyms; i++) - { - /* Ignore the following kinds of Symbols */ - if ((!symtable->n_value) /* Undefined */ - || (symtable->n_type >= N_PEXT) /* Debug symbol */ - || (!(symtable->n_type & N_EXT)) /* Local Symbol */ - ) - { - symtable++; - continue; - } - if ((addr >= symtable->n_value) && (diff >= (symtable->n_value - addr))) - { - diff = (unsigned long)symtable->n_value - addr; - nearest = symtable; - } - symtable++; - } - if (nearest) - { - info->dli_saddr = nearest->n_value + ((void *)p - addr); - info->dli_sname = (char *)(strtable + nearest->n_un.n_strx); - } - } - } - dounlock(); - return 1; -} - - -/* - * Implement the dlfunc() interface, which behaves exactly the same as - * dlsym() except that it returns a function pointer instead of a data - * pointer. This can be used by applications to avoid compiler warnings - * about undefined behavior, and is intended as prior art for future - * POSIX standardization. This function requires that all pointer types - * have the same representation, which is true on all platforms FreeBSD - * runs on, but is not guaranteed by the C standard. - */ -#if 0 -dlfunc_t dlfunc(void * dl_restrict handle, const char * dl_restrict symbol) -{ - union - { - void *d; - dlfunc_t f; - } rv; - int sym_len = strlen(symbol); - char *malloc_sym = NULL; - dolock(); - malloc_sym = malloc(sym_len + 2); - if (malloc_sym) - { - sprintf(malloc_sym, "_%s", symbol); - rv.d = dlsymIntern(handle, malloc_sym, 1); - free(malloc_sym); - } - else - { - error("Unable to allocate memory"); - goto dlfuncerror; - } - dounlock(); - return rv.f; - dlfuncerror: - dounlock(); - return NULL; -} -#endif diff --git a/http.c b/http.c deleted file mode 100644 index 6b5580d..0000000 --- a/http.c +++ /dev/null @@ -1,137 +0,0 @@ -/* Asterisk Manager Proxy - Copyright (c) 2005 David C. Troy - - This program is free software, distributed under the terms of - the GNU General Public License. - - HTTP Input Handler -*/ - -#include "astmanproxy.h" - -int ParseHTTPInput(char *buf, struct message *m) { - char *n, *v; - - /* just an empty block; go home - if ( !(*buf) ) - return 0; */ - - /* initialize message block */ - memset(m, 0, sizeof (struct message) ); - - n = buf; - while ( (v = strstr(n, "=")) ) { - v += 1; - debugmsg("n: %s, v: %s", n, v); - strncat(m->headers[m->hdrcount], n, v-n-1); - strcat(m->headers[m->hdrcount], ": "); - - if ( (n = strstr(v, "&")) ) { - n += 1; - } else { - n = (v + strlen(v) + 1); - } - strncat(m->headers[m->hdrcount], v, n-v-1); - debugmsg("got hdr: %s", m->headers[m->hdrcount]); - m->hdrcount++; - } - - return (m->hdrcount > 0); -} - -int BuildHTTPHeader(char *hdr) { - - - time_t t; - struct tm tm; - char date[80]; - - time(&t); - localtime_r(&t, &tm); - strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm); - - sprintf(hdr, - "HTTP/1.1 200 OK\r\n" - "Date: %s\r\n" - "Content-Type: text/xml\r\n" - "Connection: close\r\n" - "Server: %s/%s\r\n\r\n", - date, PROXY_BANNER, PROXY_VERSION); - - return 0; -} - -int _read(struct mansession *s, struct message *m) { - - /* Note: No single line may be longer than MAX_LEN/s->inbuf, as per get_input */ - /* No HTTP Input may be longer than BUFSIZE */ - - char line[MAX_LEN], method[10], formdata[MAX_LEN], header[MAX_LEN]; - int res, clength = 0; - - if (s->inputcomplete) - return 0; - - memset(method, 0, sizeof method); - memset(formdata, 0, sizeof formdata); - - /* for http, don't do get_input forever */ - for (;;) { - memset(line, 0, sizeof line); - res = get_input(s, line); - - if (res > 0) { - if (*line == '\0' ) { - if (*method == '\0') - break; - else { - if ( !strcasecmp(method, "POST") ) { - pthread_mutex_lock(&s->lock); - strncpy(formdata, s->inbuf, clength); - /* Move remaining data back to the front */ - memmove(s->inbuf, s->inbuf + clength+1, s->inlen - clength); - s->inlen -= clength; - pthread_mutex_unlock(&s->lock); - } - debugmsg("method: %s", method); - debugmsg("clength: %d", clength); - debugmsg("formdata: %s", formdata); - debugmsg("s->inbuf: %s", s->inbuf); - debugmsg("s->inlen: %d", s->inlen); - - BuildHTTPHeader(header); - pthread_mutex_lock(&s->lock); - s->inputcomplete = 1; - write(s->fd, header, strlen(header)); - pthread_mutex_unlock(&s->lock); - debugmsg("header: %s", header); - - /* now, let's transform and copy into a standard message block */ - res = ParseHTTPInput(formdata, m); - return res; - } - } else { - debugmsg("Got http: %s", line); - /* Do meaningful things here */ - if ( !strncmp(line,"POST",4) ) { - strncpy(method, line, 4); - } else if ( !strncmp(line,"GET",3) ) { - /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */ - strncpy(method, line, 3); - memcpy(formdata, line+6, strstr(line, " HTTP")-line-6); - } else if ( !strncasecmp(line, "Content-Length: ", 16) ) { - clength = atoi(line+16); - } - } - } else if (res < 0) - return res; - } - - return -1; -} - -int _autodisconnect() { - return 1; -} - -/* We do not define a _write or _onconnect method */ diff --git a/log.c b/log.c deleted file mode 100644 index d3f8d15..0000000 --- a/log.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "astmanproxy.h" - -#define DATEFORMAT "%b %e %T" - -extern FILE *proxylog; -extern int debug; -extern pthread_mutex_t loglock; -extern pthread_mutex_t debuglock; - -void debugmsg (const char *fmt, ...) -{ - va_list ap; - - time_t t; - struct tm tm; - char date[80]; - - if (!debug) - return; - - time(&t); - localtime_r(&t, &tm); - strftime(date, sizeof(date), DATEFORMAT, &tm); - - pthread_mutex_lock(&debuglock); - va_start(ap, fmt); - printf("%s: ", date); - vprintf(fmt, ap); - printf("\n"); - va_end(ap); - pthread_mutex_unlock(&debuglock); -} - - -void logmsg (const char *fmt, ...) -{ - va_list ap; - - time_t t; - struct tm tm; - char date[80]; - - time(&t); - localtime_r(&t, &tm); - strftime(date, sizeof(date), DATEFORMAT, &tm); - - if (proxylog) { - pthread_mutex_lock(&loglock); - va_start(ap, fmt); - fprintf(proxylog, "%s: ", date); - vfprintf(proxylog, fmt, ap); - fprintf(proxylog, "\n"); - va_end(ap); - fflush(proxylog); - pthread_mutex_unlock(&loglock); - } -} diff --git a/poll-compat.h b/poll-compat.h deleted file mode 100644 index 79eab15..0000000 --- a/poll-compat.h +++ /dev/null @@ -1,101 +0,0 @@ -/*---------------------------------------------------------------------------*\ - $Id: poll-compat.h,v 1.1 2003/10/26 18:50:49 markster Exp $ - - NAME - - poll - select(2)-based poll() emulation function for BSD systems. - - SYNOPSIS - #include "poll.h" - - struct pollfd - { - int fd; - short events; - short revents; - } - - int poll (struct pollfd *pArray, unsigned long n_fds, int timeout) - - DESCRIPTION - - This file, and the accompanying "poll.c", implement the System V - poll(2) system call for BSD systems (which typically do not provide - poll()). Poll() provides a method for multiplexing input and output - on multiple open file descriptors; in traditional BSD systems, that - capability is provided by select(). While the semantics of select() - differ from those of poll(), poll() can be readily emulated in terms - of select() -- which is how this function is implemented. - - REFERENCES - Stevens, W. Richard. Unix Network Programming. Prentice-Hall, 1990. - - NOTES - 1. This software requires an ANSI C compiler. - - LICENSE - - This software is released under the following license: - - Copyright (c) 1995-2002 Brian M. Clapper - All rights reserved. - - Redistribution and use in source and binary forms are - permitted provided that: (1) source distributions retain - this entire copyright notice and comment; (2) modifications - made to the software are prominently mentioned, and a copy - of the original software (or a pointer to its location) are - included; and (3) distributions including binaries display - the following acknowledgement: "This product includes - software developed by Brian M. Clapper " - in the documentation or other materials provided with the - distribution. The name of the author may not be used to - endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS - OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE. - - Effectively, this means you can do what you want with the software - except remove this notice or take advantage of the author's name. - If you modify the software and redistribute your modified version, - you must indicate that your version is a modification of the - original, and you must provide either a pointer to or a copy of the - original. -\*---------------------------------------------------------------------------*/ - -#ifndef _POLL_EMUL_H_ -#define _POLL_EMUL_H_ - -#define POLLIN 0x01 -#define POLLPRI 0x02 -#define POLLOUT 0x04 -#define POLLERR 0x08 -#define POLLHUP 0x10 -#define POLLNVAL 0x20 - -struct pollfd -{ - int fd; - short events; - short revents; -}; - -#ifdef __cplusplus -extern "C" -{ -#endif - -#if (__STDC__ > 0) || defined(__cplusplus) -extern int poll (struct pollfd *pArray, unsigned long n_fds, int timeout); -#else -extern int poll(); -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* _POLL_EMUL_H_ */ diff --git a/poll.c b/poll.c deleted file mode 100644 index a36539a..0000000 --- a/poll.c +++ /dev/null @@ -1,306 +0,0 @@ -/*---------------------------------------------------------------------------*\ - $Id: poll.c,v 1.1 2003/10/26 19:17:28 tholo Exp $ - - NAME - - poll - select(2)-based poll() emulation function for BSD systems. - - SYNOPSIS - #include "poll.h" - - struct pollfd - { - int fd; - short events; - short revents; - } - - int poll (struct pollfd *pArray, unsigned long n_fds, int timeout) - - DESCRIPTION - - This file, and the accompanying "poll.h", implement the System V - poll(2) system call for BSD systems (which typically do not provide - poll()). Poll() provides a method for multiplexing input and output - on multiple open file descriptors; in traditional BSD systems, that - capability is provided by select(). While the semantics of select() - differ from those of poll(), poll() can be readily emulated in terms - of select() -- which is how this function is implemented. - - REFERENCES - Stevens, W. Richard. Unix Network Programming. Prentice-Hall, 1990. - - NOTES - 1. This software requires an ANSI C compiler. - - LICENSE - - This software is released under the following license: - - Copyright (c) 1995-2002 Brian M. Clapper - All rights reserved. - - Redistribution and use in source and binary forms are - permitted provided that: (1) source distributions retain - this entire copyright notice and comment; (2) modifications - made to the software are prominently mentioned, and a copy - of the original software (or a pointer to its location) are - included; and (3) distributions including binaries display - the following acknowledgement: "This product includes - software developed by Brian M. Clapper " - in the documentation or other materials provided with the - distribution. The name of the author may not be used to - endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS - OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE. - - Effectively, this means you can do what you want with the software - except remove this notice or take advantage of the author's name. - If you modify the software and redistribute your modified version, - you must indicate that your version is a modification of the - original, and you must provide either a pointer to or a copy of the - original. -\*---------------------------------------------------------------------------*/ - - -/*---------------------------------------------------------------------------*\ - Includes -\*---------------------------------------------------------------------------*/ - -#include /* standard Unix definitions */ -#include /* system types */ -#include /* time definitions */ -#include /* assertion macros */ -#include /* string functions */ - -#include "poll-compat.h" /* this package */ - -/*---------------------------------------------------------------------------*\ - Macros -\*---------------------------------------------------------------------------*/ - -#ifndef MAX -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#endif - - -/*---------------------------------------------------------------------------*\ - Private Functions -\*---------------------------------------------------------------------------*/ - -static int map_poll_spec -#if __STDC__ > 0 - (struct pollfd *pArray, - unsigned long n_fds, - fd_set *pReadSet, - fd_set *pWriteSet, - fd_set *pExceptSet) -#else - (pArray, n_fds, pReadSet, pWriteSet, pExceptSet) - struct pollfd *pArray; - unsigned long n_fds; - fd_set *pReadSet; - fd_set *pWriteSet; - fd_set *pExceptSet; -#endif -{ - register unsigned long i; /* loop control */ - register struct pollfd *pCur; /* current array element */ - register int max_fd = -1; /* return value */ - - /* - Map the poll() structures into the file descriptor sets required - by select(). - */ - for (i = 0, pCur = pArray; i < n_fds; i++, pCur++) - { - /* Skip any bad FDs in the array. */ - - if (pCur->fd < 0) - continue; - - if (pCur->events & POLLIN) - { - /* "Input Ready" notification desired. */ - FD_SET (pCur->fd, pReadSet); - } - - if (pCur->events & POLLOUT) - { - /* "Output Possible" notification desired. */ - FD_SET (pCur->fd, pWriteSet); - } - - if (pCur->events & POLLPRI) - { - /* - "Exception Occurred" notification desired. (Exceptions - include out of band data. - */ - FD_SET (pCur->fd, pExceptSet); - } - - max_fd = MAX (max_fd, pCur->fd); - } - - return max_fd; -} - -static struct timeval *map_timeout -#if __STDC__ > 0 - (int poll_timeout, struct timeval *pSelTimeout) -#else - (poll_timeout, pSelTimeout) - int poll_timeout; - struct timeval *pSelTimeout; -#endif -{ - struct timeval *pResult; - - /* - Map the poll() timeout value into a select() timeout. The possible - values of the poll() timeout value, and their meanings, are: - - VALUE MEANING - - -1 wait indefinitely (until signal occurs) - 0 return immediately, don't block - >0 wait specified number of milliseconds - - select() uses a "struct timeval", which specifies the timeout in - seconds and microseconds, so the milliseconds value has to be mapped - accordingly. - */ - - assert (pSelTimeout != (struct timeval *) NULL); - - switch (poll_timeout) - { - case -1: - /* - A NULL timeout structure tells select() to wait indefinitely. - */ - pResult = (struct timeval *) NULL; - break; - - case 0: - /* - "Return immediately" (test) is specified by all zeros in - a timeval structure. - */ - pSelTimeout->tv_sec = 0; - pSelTimeout->tv_usec = 0; - pResult = pSelTimeout; - break; - - default: - /* Wait the specified number of milliseconds. */ - pSelTimeout->tv_sec = poll_timeout / 1000; /* get seconds */ - poll_timeout %= 1000; /* remove seconds */ - pSelTimeout->tv_usec = poll_timeout * 1000; /* get microseconds */ - pResult = pSelTimeout; - break; - } - - - return pResult; -} - -static void map_select_results -#if __STDC__ > 0 - (struct pollfd *pArray, - unsigned long n_fds, - fd_set *pReadSet, - fd_set *pWriteSet, - fd_set *pExceptSet) -#else - (pArray, n_fds, pReadSet, pWriteSet, pExceptSet) - struct pollfd *pArray; - unsigned long n_fds; - fd_set *pReadSet; - fd_set *pWriteSet; - fd_set *pExceptSet; -#endif -{ - register unsigned long i; /* loop control */ - register struct pollfd *pCur; /* current array element */ - - for (i = 0, pCur = pArray; i < n_fds; i++, pCur++) - { - /* Skip any bad FDs in the array. */ - - if (pCur->fd < 0) - continue; - - /* Exception events take priority over input events. */ - - pCur->revents = 0; - if (FD_ISSET (pCur->fd, pExceptSet)) - pCur->revents |= POLLPRI; - - else if (FD_ISSET (pCur->fd, pReadSet)) - pCur->revents |= POLLIN; - - if (FD_ISSET (pCur->fd, pWriteSet)) - pCur->revents |= POLLOUT; - } - - return; -} - -/*---------------------------------------------------------------------------*\ - Public Functions -\*---------------------------------------------------------------------------*/ - -int poll - -#if __STDC__ > 0 - (struct pollfd *pArray, unsigned long n_fds, int timeout) -#else - (pArray, n_fds, timeout) - struct pollfd *pArray; - unsigned long n_fds; - int timeout; -#endif - -{ - fd_set read_descs; /* input file descs */ - fd_set write_descs; /* output file descs */ - fd_set except_descs; /* exception descs */ - struct timeval stime; /* select() timeout value */ - int ready_descriptors; /* function result */ - int max_fd; /* maximum fd value */ - struct timeval *pTimeout; /* actually passed */ - - FD_ZERO (&read_descs); - FD_ZERO (&write_descs); - FD_ZERO (&except_descs); - - assert (pArray != (struct pollfd *) NULL); - - /* Map the poll() file descriptor list in the select() data structures. */ - - max_fd = map_poll_spec (pArray, n_fds, - &read_descs, &write_descs, &except_descs); - - /* Map the poll() timeout value in the select() timeout structure. */ - - pTimeout = map_timeout (timeout, &stime); - - /* Make the select() call. */ - - ready_descriptors = select (max_fd + 1, &read_descs, &write_descs, - &except_descs, pTimeout); - - if (ready_descriptors >= 0) - { - map_select_results (pArray, n_fds, - &read_descs, &write_descs, &except_descs); - } - - return ready_descriptors; -} diff --git a/proxyfunc.c b/proxyfunc.c deleted file mode 100644 index 11132ff..0000000 --- a/proxyfunc.c +++ /dev/null @@ -1,258 +0,0 @@ -#include "astmanproxy.h" - -extern struct mansession *sessions; -extern struct iohandler *iohandlers; -extern pthread_mutex_t serverlock; - -void *ProxyListIOHandlers(struct mansession *s) { - struct message m; - struct iohandler *i; - - memset(&m, 0, sizeof(struct message)); - AddHeader(&m, "ProxyResponse: Success"); - - i = iohandlers; - while (i && (m.hdrcount < MAX_HEADERS - 1) ) { - if (i->read) - AddHeader(&m, "InputHandler: %s", i->formatname); - if (i->write) - AddHeader(&m, "OutputHandler: %s", i->formatname); - i = i->next; - } - - s->output->write(s, &m); - return 0; -} - -void *ProxyListSessions(struct mansession *s) { - struct message m; - struct mansession *c; - char iabuf[INET_ADDRSTRLEN]; - - memset(&m, 0, sizeof(struct message)); - AddHeader(&m, "ProxyResponse: Success"); - - c = sessions; - while (c && (m.hdrcount < MAX_HEADERS - 1) ) { - if (!c->server) { - AddHeader(&m, "ProxyClientSession: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr), c->actionid); - AddHeader(&m, "ProxyClientInputHandler: %s", c->input->formatname); - AddHeader(&m, "ProxyClientOutputHandler: %s", c->output->formatname); - } else - AddHeader(&m, "ProxyServerSession: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); - c = c->next; - } - s->output->write(s, &m); - return 0; -} - -void *ProxySetOutputFormat(struct mansession *s, struct message *m) { - struct message mo; - char *value; - - value = astman_get_header(m, "OutputFormat"); - SetIOHandlers(s, s->input->formatname, value); - /* TODO: this is retarded */ - - memset(&mo, 0, sizeof(struct message)); - AddHeader(&mo, "ProxyResponse: Success"); - AddHeader(&mo, "OutputFormat: %s", s->output->formatname ); - - s->output->write(s, &mo); - - return 0; -} - -void *ProxySetAutoFilter(struct mansession *s, struct message *m) { - struct message mo; - char *value; - int i; - - value = astman_get_header(m, "AutoFilter"); - if ( !strcasecmp(value, "on") ) - i = 1; - else - i = 0; - pthread_mutex_lock(&s->lock); - s->autofilter = i; - pthread_mutex_unlock(&s->lock); - - memset(&mo, 0, sizeof(struct message)); - AddHeader(&mo, "ProxyResponse: Success"); - AddHeader(&mo, "AutoFilter: %d", s->autofilter); - - s->output->write(s, &mo); - - return 0; -} - -void *ProxyLogin(struct mansession *s) { - struct message m; - - /* Send back dummy output for clients that insist on authenticating the old way */ - /* Mostly, we want to avoid sending this to Asterisk */ - - /* Response: Success */ - /* Message: Authentication accepted */ - - memset(&m, 0, sizeof(struct message)); - AddHeader(&m, "Response: Success"); - AddHeader(&m, "Message: Authentication accepted"); - - s->output->write(s, &m); - - return 0; -} - -void *ProxyLogoff(struct mansession *s) { - struct message m; - - memset(&m, 0, sizeof(struct message)); - AddHeader(&m, "Goodbye: Y'all come back now, y'hear?"); - - s->output->write(s, &m); - - destroy_session(s); - if (debug) - debugmsg("Client logged off - exiting thread"); - pthread_exit(NULL); - return 0; -} - -int ProxyAddServer(struct mansession *s, struct message *m) { - struct message mo; - struct ast_server *srv; - int res = 0; - - /* malloc ourselves a server credentials structure */ - srv = malloc(sizeof(struct ast_server)); - if ( !srv ) { - fprintf(stderr, "Failed to allocate server credentials: %s\n", strerror(errno)); - exit(1); - } - memset(srv, 0, sizeof (struct ast_server) ); - - - /* TODO: Disallow adding of duplicate servers? Or not, I suppose that could be useful (events on/off) */ - memset(srv, 0, sizeof(struct ast_server) ); - memset(&mo, 0, sizeof(struct message)); - strcpy(srv->ast_host, astman_get_header(m, "Server")); - strcpy(srv->ast_user, astman_get_header(m, "Username")); - strcpy(srv->ast_pass, astman_get_header(m, "Secret")); - strcpy(srv->ast_port, astman_get_header(m, "Port")); - strcpy(srv->ast_events, astman_get_header(m, "Events")); - - if (*srv->ast_host && *srv->ast_user && *srv->ast_pass && *srv->ast_port && *srv->ast_events) { - pthread_mutex_lock(&serverlock); - srv->next = pc.serverlist; - pc.serverlist = srv; - pthread_mutex_unlock(&serverlock); - res = StartServer(srv); - } else - res = 1; - - if (res) { - AddHeader(&mo, "ProxyResponse: Failure"); - AddHeader(&mo, "Message: Could not add %s", srv->ast_host); - } else { - AddHeader(&mo, "ProxyResponse: Success"); - AddHeader(&mo, "Message: Added %s", srv->ast_host); - } - - s->output->write(s, &mo); - return 0; -} - -int ProxyDropServer(struct mansession *s, struct message *m) { - struct message mo; - struct mansession *srv; - char *value; - int res; - - memset(&mo, 0, sizeof(struct message)); - value = astman_get_header(m, "Server"); - srv = sessions; - while (*value && srv) { - if (srv->server && !strcmp(srv->server->ast_host, value)) - break; - srv = srv->next; - } - - if (srv) { - destroy_session(srv); - debugmsg("Dropping Server %s", value); - AddHeader(&mo, "ProxyResponse: Success"); - AddHeader(&mo, "Message: Dropped %s", value); - res = 0; - } else { - debugmsg("Failed to Drop Server %s -- not found", value); - AddHeader(&mo, "ProxyResponse: Failure"); - AddHeader(&mo, "Message: Cannot Drop Server %s, Does Not Exist", value); - res = 1; - } - - s->output->write(s, &mo); - return res; -} - -void *ProxyListServers(struct mansession *s) { - struct message m; - struct mansession *c; - char iabuf[INET_ADDRSTRLEN]; - - memset(&m, 0, sizeof(struct message)); - AddHeader(&m, "ProxyResponse: Success"); - - c = sessions; - while (c) { - if (c->server) { - AddHeader(&m, "ProxyListServer I: %s H: %s U: %s P: %s E: %s ", - ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr), - c->server->ast_host, c->server->ast_user, - c->server->ast_port, c->server->ast_events); - } - - c = c->next; - } - s->output->write(s, &m); - return 0; -} - - -void *proxyaction_do(char *proxyaction, struct message *m, struct mansession *s) -{ - if (!strcasecmp(proxyaction,"SetOutputFormat")) - ProxySetOutputFormat(s, m); - else if (!strcasecmp(proxyaction,"SetAutoFilter")) - ProxySetAutoFilter(s, m); - else if (!strcasecmp(proxyaction,"ListSessions")) - ProxyListSessions(s); - else if (!strcasecmp(proxyaction,"AddServer")) - ProxyAddServer(s, m); - else if (!strcasecmp(proxyaction,"DropServer")) - ProxyDropServer(s, m); - else if (!strcasecmp(proxyaction,"ListServers")) - ProxyListServers(s); - else if (!strcasecmp(proxyaction,"ListIOHandlers")) - ProxyListIOHandlers(s); - else if (!strcasecmp(proxyaction,"Logoff")) - ProxyLogoff(s); - else - proxyerror_do(s, "Invalid Proxy Action"); - - return 0; -} - -int proxyerror_do(struct mansession *s, char *err) -{ - struct message mo; - - memset(&mo, 0, sizeof(struct message)); - AddHeader(&mo, "ProxyResponse: Error"); - AddHeader(&mo, "Message: %s", err); - - s->output->write(s, &mo); - - return 0; -} - diff --git a/src/astmanproxy.c b/src/astmanproxy.c new file mode 100644 index 0000000..6c82594 --- /dev/null +++ b/src/astmanproxy.c @@ -0,0 +1,676 @@ +/* Asterisk Manager Proxy + Copyright (c) 2005 David C. Troy + + This program is free software, distributed under the terms of + the GNU General Public License. + +*/ + +#include "astmanproxy.h" + +extern int LoadHandlers( void ); +extern void ReadConfig( void ); +extern void ReadPerms( void ); +extern FILE *OpenLogfile( void ); +extern int SetProcUID( void ); + +extern void *proxyaction_do(char *proxyaction, struct message *m, struct mansession *s); +extern void *ProxyLogin(struct mansession *s, char* username, char* secret); +extern void *ProxyLogoff(struct mansession *s); +extern int ValidateAction(struct message *m, struct mansession *s, int inbound); +extern void *SendError(struct mansession *s); + +int ConnectAsterisk(struct mansession *s); + +struct proxyconfig pc; +struct mansession *sessions = NULL; +struct iohandler *iohandlers = NULL; + +pthread_mutex_t sessionlock; +pthread_mutex_t serverlock; +pthread_mutex_t userslock; +pthread_mutex_t loglock; +pthread_mutex_t debuglock; +static int asock = -1; +FILE *proxylog; +int debug = 0; + +void hup(int sig) { + if (proxylog) { + fflush(proxylog); + fclose(proxylog); + } + proxylog = OpenLogfile(); + logmsg("Received HUP -- reopened log"); + ReadPerms(); + logmsg("Received HUP -- reread permissions"); +} + +void leave(int sig) { + struct mansession *c; + struct message sm, cm; + struct iohandler *io; + struct ast_server *srv; + char iabuf[INET_ADDRSTRLEN]; + + /* Message to send to servers */ + memset(&sm, 0, sizeof(struct message)); + AddHeader(&sm, "Action: Logoff"); + + /* Message to send to clients */ + memset(&cm, 0, sizeof(struct message)); + AddHeader(&cm, PROXY_SHUTDOWN); + + if (debug) + debugmsg("Notifying and closing sessions"); + pthread_mutex_lock (&sessionlock); + while (sessions) { + c = sessions; + sessions = sessions->next; + + if (c->server) { + if (debug) + debugmsg("asterisk@%s: closing session", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); + c->output->write(c, &sm); + logmsg("Shutdown, closed server %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); + } else { + if (debug) + debugmsg("client@%s: closing session", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); + c->output->write(c, &cm); + logmsg("Shutdown, closed client %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); + } + close(c->fd); + pthread_mutex_destroy(&c->lock); + free(c); + } + pthread_mutex_unlock (&sessionlock); + + /* unload server list */ + while (pc.serverlist) { + srv = pc.serverlist; + pc.serverlist = srv->next; + if (debug) + debugmsg("asterisk@%s: forgetting", srv->ast_host); + free(srv); + } + + if (debug) + debugmsg("Closing listener socket"); + close(asock); + + /* unload io handlers */ + while (iohandlers) { + io = iohandlers; + iohandlers = iohandlers->next; + if (debug) + debugmsg("unloading: %s", io->formatname); + dlclose(io->dlhandle); + free(io); + } + + if(debug) + debugmsg("Done!\n"); + logmsg("Proxy stopped; shutting down."); + + fclose(proxylog); + pthread_mutex_destroy(&sessionlock); + pthread_mutex_destroy(&loglock); + pthread_mutex_destroy(&debuglock); + exit(sig); +} + +void Version( void ) +{ + printf("astmanproxy: Version %s, (C) David C. Troy\n", PROXY_VERSION); + return; +} + +void Usage( void ) +{ + printf("Usage: astmanproxy [-d|-h|-v]\n"); + printf(" -d : Start in Debug Mode\n"); + printf(" -h : Displays this message\n"); + printf(" -v : Displays version information\n"); + printf("Start with no options to run as daemon\n"); + return; +} + +void destroy_session(struct mansession *s) +{ + struct mansession *cur, *prev = NULL; + char iabuf[INET_ADDRSTRLEN]; + + pthread_mutex_lock(&sessionlock); + cur = sessions; + while(cur) { + if (cur == s) + break; + prev = cur; + cur = cur->next; + } + if (cur) { + if (prev) + prev->next = cur->next; + else + sessions = cur->next; + debugmsg("Connection closed: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); + if (s->fd > -1) + close(s->fd); + pthread_mutex_destroy(&s->lock); + free(s); + } else + debugmsg("Trying to delete non-existent session %p?\n", s); + pthread_mutex_unlock(&sessionlock); + + /* If there are no servers and no clients, why are we here? */ + if (!sessions) { + logmsg("Cannot connect to any servers! Leaving!"); + leave(0); + } +} + +int WriteClients(struct message *m) { + struct mansession *c; + char *actionid; + + c = sessions; + while (c) { + if ( !c->server && m->hdrcount>1 && ValidateAction(m, c, 1) ) { + if (c->autofilter && c->actionid) { + actionid = astman_get_header(m, ACTION_ID); + if ( !strcmp(actionid, c->actionid) ) { + c->output->write(c, m); + } + } else + c->output->write(c, m); + if ( c->input->autodisconnect && c->input->autodisconnect() ) + close(c->fd); + } + c = c->next; + } + return 1; +} + +int WriteAsterisk(struct message *m) { + int i; + char outstring[MAX_LEN], *dest; + struct mansession *s, *first; + + first = NULL; + dest = NULL; + + s = sessions; + + dest = astman_get_header(m, "Server"); + if (debug && *dest) debugmsg("set destination: %s", dest); + while ( s ) { + if ( s->server && (s->connected > 0) ) { + if ( !first ) + first = s; + if (*dest && !strcasecmp(dest, s->server->ast_host) ) + break; + } + s = s->next; + } + + if (!s) + s = first; + + /* Check for no servers and empty block -- Don't pester Asterisk if it is one*/ + if (!s || !s->server || (!m->hdrcount && !m->headers[0][0]) ) + return 1; + + debugmsg("writing block to %s", s->server->ast_host); + + pthread_mutex_lock(&s->lock); + for (i=0; ihdrcount; i++) { + if (strcasecmp(m->headers[i], "Server:") ) { + sprintf(outstring, "%s\r\n", m->headers[i]); + write(s->fd, outstring, strlen(outstring) ); + } + } + write(s->fd, "\r\n", 2); + pthread_mutex_unlock(&s->lock); + return 1; +} + +void *setactionid(char *actionid, struct message *m, struct mansession *s) +{ + pthread_mutex_lock(&s->lock); + strncpy(s->actionid, actionid, MAX_LEN); + pthread_mutex_unlock(&s->lock); + + return 0; +} + +/* Handles proxy client sessions; closely based on session_do from asterisk's manager.c */ +void *session_do(struct mansession *s) +{ + struct message m; + int res; + char *proxyaction, *actionid, *action, *key; + + if (s->input->onconnect) + s->input->onconnect(s, &m); + + for (;;) { + /* Get a complete message block from input handler */ + memset(&m, 0, sizeof(struct message) ); + res = s->input->read(s, &m); + m.session = s; + + if (res > 0) { + /* Check for anything that requires proxy-side processing */ + if (pc.key && !s->authenticated) { + key = astman_get_header(&m, "ProxyKey"); + if (!strcmp(key, pc.key) ) { + pthread_mutex_lock(&s->lock); + s->authenticated = 1; + pthread_mutex_unlock(&s->lock); + } else + break; + } + + proxyaction = astman_get_header(&m, "ProxyAction"); + actionid = astman_get_header(&m, ACTION_ID); + action = astman_get_header(&m, "Action"); + if ( !strcasecmp(action, "Login") ) + ProxyLogin(s, astman_get_header(&m, "UserName"), astman_get_header(&m, "Secret")); + else if ( !strcasecmp(action, "Logoff") ) + ProxyLogoff(s); + else if ( !(*proxyaction == '\0') ) + proxyaction_do(proxyaction, &m, s); + else if ( ValidateAction(&m, s ,0) ) { + if ( !(*actionid == '\0') ) + setactionid(actionid, &m, s); + if ( !WriteAsterisk(&m) ) + break; + } else { + SendError(s); + } + } else if (res < 0) + break; + } + + destroy_session(s); + if (debug) + debugmsg("Exiting session_do thread"); + pthread_exit(NULL); + return NULL; +} + +void *HandleAsterisk(struct mansession *s) +{ + struct message m; + int res,i; + char iabuf[INET_ADDRSTRLEN]; + + if (ConnectAsterisk(s)) + goto leave; + for (;;) { + + debugmsg("asterisk@%s: attempting read...", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); + memset(&m, 0, sizeof(struct message) ); + res = s->input->read(s, &m); + m.session = s; + + if (res > 0) { + if (debug) { + for(i=0; isin.sin_addr), m.headers[i]); + } + } + + if (!s->connected) { + if ( !strcmp("Authentication accepted", astman_get_header(&m, "Message")) ) { + s->connected = 1; + if (debug) + debugmsg("asterisk@%s: connected successfully!", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr) ); + } + if ( !strcmp("Authentication failed", astman_get_header(&m, "Message")) ) { + s->connected = -1; + } + } + + m.session = s; + AddHeader(&m, "Server: %s", m.session->server->ast_host); + + if (!WriteClients(&m)) + break; + /* TODO: does it make any sense to abort * conn if we cannot write to clients? I don't think so. + Do we even get a return code back that means anything? I don't think so. */ + } else if (res < 0) { + if (debug) + debugmsg("asterisk@%s: Not connected", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); + if ( ConnectAsterisk(s) ) + break; + } + } + +leave: + if (debug) + debugmsg("asterisk@%s: Giving up and exiting thread", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr) ); + destroy_session(s); + pthread_exit(NULL); + return NULL; +} + +int ConnectAsterisk(struct mansession *s) { + char iabuf[INET_ADDRSTRLEN]; + int r = 1, res = 0; + struct message m; + + /* Don't try to do this if auth has already failed! */ + if (s->connected < 0 ) + return 1; + else + s->connected = 0; + + if (debug) + debugmsg("asterisk@%s: Connecting (u=%s, p=%s)", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), + s->server->ast_user, s->server->ast_pass); + + /* Construct auth message just once */ + memset( &m, 0, sizeof(struct message) ); + AddHeader(&m, "Action: Login"); + AddHeader(&m, "Username: %s", s->server->ast_user); + AddHeader(&m, "Secret: %s", s->server->ast_pass); + AddHeader(&m, "Events: %s", s->server->ast_events); + + for ( ;; ) { + if ( connect_nonb(s->fd, (struct sockaddr *) &s->sin, sizeof(s->sin), 2) < 0 ) { + if (errno == EISCONN) { + pthread_mutex_lock(&s->lock); + s->fd = socket(AF_INET, SOCK_STREAM, 0); + pthread_mutex_unlock(&s->lock); + } + if (debug) + debugmsg("asterisk@%s: Connect failed, Retrying (%d) %s", + ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), r, strerror(errno) ); + if (pc.maxretries && (++r>pc.maxretries) ) { + res = 1; + break; + } else + sleep(pc.retryinterval); + } else { + /* Send login */ + s->output->write(s, &m); + res = 0; + break; + } + } + + return res; +} + +int StartServer(struct ast_server *srv) { + + struct mansession *s; + struct hostent *ast_hostent; + + char iabuf[INET_ADDRSTRLEN]; + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + ast_hostent = gethostbyname(srv->ast_host); + if (!ast_hostent) { + logmsg("Cannot resolve host %s, cannot add!", srv->ast_host); + debugmsg("Cannot resolve host %s, cannot add!", srv->ast_host); + return 1; + } + + s = malloc(sizeof(struct mansession)); + if ( !s ) { + logmsg("Failed to allocate server session: %s\n", strerror(errno)); + debugmsg("Failed to allocate server session: %s\n", strerror(errno)); + return 1; + } + + memset(s, 0, sizeof(struct mansession)); + SetIOHandlers(s, "standard", "standard"); + s->server = srv; + + bzero((char *) &s->sin,sizeof(s->sin)); + s->sin.sin_family = AF_INET; + memcpy( &s->sin.sin_addr.s_addr, ast_hostent->h_addr, ast_hostent->h_length ); + s->sin.sin_port = htons(atoi(s->server->ast_port)); + s->fd = socket(AF_INET, SOCK_STREAM, 0); + + pthread_mutex_lock(&sessionlock); + s->next = sessions; + sessions = s; + pthread_mutex_unlock(&sessionlock); + + logmsg("Allocated Asterisk server session for %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); + if (debug) { + debugmsg("asterisk@%s: Allocated server session", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); + debugmsg("Set %s input format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->input->formatname); + debugmsg("Set %s output format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->output->formatname); + } + + + if (pthread_create(&s->t, &attr, (void *)HandleAsterisk, s)) + destroy_session(s); + else + debugmsg("launched ast %s thread!", s->server->ast_host); + + pthread_attr_destroy(&attr); + return 0; +} + +int LaunchAsteriskThreads() { + + struct ast_server *srv; + + srv = pc.serverlist; + while (srv) { + StartServer(srv); + srv = srv->next; + } + return 0; +} + +int SetIOHandlers(struct mansession *s, char *ifmt, char *ofmt) +{ + int res = 0; + struct iohandler *io; + + io = iohandlers; + pthread_mutex_lock(&s->lock); + while (io) { + if ( !strcasecmp(io->formatname, ifmt) ) + s->input = io; + + if ( !strcasecmp(io->formatname, ofmt) ) + s->output = io; + + io = io->next; + } + + if (!s->output) { + /* TODO: Output debug that default/first handler was used */ + s->output = iohandlers; + res = 1; + } + + if (!s->input) { + /* TODO: Output debug that default/first handler was used */ + s->input = iohandlers; + res = 1; + } + pthread_mutex_unlock(&s->lock); + + return res; +} + +static void *accept_thread() +{ + int as; + struct sockaddr_in sin; + int sinlen; + struct mansession *s; + struct protoent *p; + int arg = 1; + int flags; + pthread_attr_t attr; + char iabuf[INET_ADDRSTRLEN]; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + for (;;) { + sinlen = sizeof(sin); + as = accept(asock, (struct sockaddr *)&sin, &sinlen); + if (as < 0) { + logmsg("Accept returned -1: %s\n", strerror(errno)); + continue; + } + p = (struct protoent *)getprotobyname("tcp"); + if( p ) { + if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) { + logmsg("Failed to set listener tcp connection to TCP_NODELAY mode: %s\n", strerror(errno)); + } + } + s = malloc(sizeof(struct mansession)); + if ( !s ) { + logmsg("Failed to allocate listener session: %s\n", strerror(errno)); + continue; + } + memset(s, 0, sizeof(struct mansession)); + memcpy(&s->sin, &sin, sizeof(sin)); + + /* For safety, make sure socket is non-blocking */ + flags = fcntl(as, F_GETFL); + fcntl(as, F_SETFL, flags | O_NONBLOCK); + + pthread_mutex_init(&s->lock, NULL); + s->fd = as; + SetIOHandlers(s, pc.inputformat, pc.outputformat); + s->autofilter = pc.autofilter; + s->inputcomplete = 0; + s->server = NULL; + + pthread_mutex_lock(&sessionlock); + s->next = sessions; + sessions = s; + pthread_mutex_unlock(&sessionlock); + + logmsg("Connection received from %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); + if (debug) { + debugmsg("Connection received from %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); + debugmsg("Set %s input format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->input->formatname); + debugmsg("Set %s output format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->output->formatname); + } + + if (pthread_create(&s->t, &attr, (void *)session_do, s)) + destroy_session(s); + } + pthread_attr_destroy(&attr); + return NULL; +} + +int main(int argc, char *argv[]) +{ + struct sockaddr_in serv_sock_addr, client_sock_addr; + int cli_addrlen; + struct linger lingerstruct; /* for socket reuse */ + int flag; /* for socket reuse */ + pid_t pid; + char i; + + /* Figure out if we are in debug mode, handle other switches */ + while (( i = getopt( argc, argv, "dhv" ) ) != EOF ) + { + switch( i ) { + case 'd': + debug = 1; + break; + case 'h': + Usage(); + exit(0); + case 'v': + Version(); + exit(0); + case '?': + Usage(); + exit(1); + } + } + + + ReadConfig(); + proxylog = OpenLogfile(); + LoadHandlers(); + + if (SetProcUID()) { + fprintf(stderr,"Cannot set user/group! Check proc_user and proc_group config setting!\n"); + exit(1); + } + + /* If we are not in debug mode, then fork to background */ + if (!debug) { + if ( (pid = fork()) < 0) + exit( 1 ); + else if ( pid > 0) + exit( 0 ); + } + + /* Setup signal handlers */ + (void) signal(SIGINT,leave); + (void) signal(SIGHUP,hup); + (void) signal(SIGTERM,leave); + (void) signal(SIGPIPE, SIG_IGN); + + /* Initialize global mutexes */ + pthread_mutex_init(&sessionlock, NULL); + pthread_mutex_init(&userslock, NULL); + pthread_mutex_init(&loglock, NULL); + pthread_mutex_init(&debuglock, NULL); + + /* Read initial state for user permissions */ + ReadPerms(); + + /* Initialize global client/server list */ + sessions = NULL; + LaunchAsteriskThreads(); + + /* Setup listener socket to setup new sessions... */ + if ((asock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + fprintf(stderr,"Cannot create listener socket!\n"); + exit(1); + } + bzero((char *) &serv_sock_addr, sizeof serv_sock_addr ); + serv_sock_addr.sin_family = AF_INET; + + if ( !strcmp(pc.listen_addr,"*") ) + serv_sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); + else + serv_sock_addr.sin_addr.s_addr = inet_addr( pc.listen_addr); + serv_sock_addr.sin_port = htons((short)pc.listen_port); + + /* Set listener socket re-use options */ + setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag)); + lingerstruct.l_onoff = 1; + lingerstruct.l_linger = 5; + setsockopt(asock, SOL_SOCKET, SO_LINGER, (void *)&lingerstruct, sizeof(lingerstruct)); + + if (bind(asock, (struct sockaddr *) &serv_sock_addr, sizeof serv_sock_addr ) < 0) { + fprintf(stderr,"Cannot bind to listener socket!\n"); + exit(1); + } + + listen(asock, 5); + cli_addrlen = sizeof(client_sock_addr); + if (debug) + debugmsg("Listening for connections"); + logmsg("Proxy Started: Listening for connections"); + + /* Launch listener thread */ + accept_thread(); + + pthread_exit(NULL); + exit(0); +} + diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..5f63c15 --- /dev/null +++ b/src/common.c @@ -0,0 +1,147 @@ +#include "astmanproxy.h" + +/* This routine based on get_input from Asterisk manager.c */ +/* Good generic line-based input routine for \r\n\r\n terminated input */ +/* Used by standard.c and other input handlers */ +int get_input(struct mansession *s, char *output) +{ + /* output must have at least sizeof(s->inbuf) space */ + int res; + int x; + struct pollfd fds[1]; + char iabuf[INET_ADDRSTRLEN]; + + debugmsg("in get_input"); + + /* Look for \r\n from the front, our preferred end of line */ + for (x=0;xinlen;x++) { + int xtra = 0; + if (s->inbuf[x] == '\n') { + if (x && s->inbuf[x-1] == '\r') { + xtra = 1; + } + /* Copy output data not including \r\n */ + memcpy(output, s->inbuf, x - xtra); + /* Add trailing \0 */ + output[x-xtra] = '\0'; + /* Move remaining data back to the front */ + memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x); + s->inlen -= (x + 1); + return 1; + } + } + + if (s->inlen >= sizeof(s->inbuf) - 1) { + if (debug) + debugmsg("Warning: Got long line with no end from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf); + s->inlen = 0; + } + debugmsg("attempting poll operation"); + fds[0].fd = s->fd; + fds[0].events = POLLIN; + res = poll(fds, 1, -1); + debugmsg("returned from poll op"); + if (res < 0 && debug) { + debugmsg("Select returned error"); + } else if (res > 0) { + pthread_mutex_lock(&s->lock); + debugmsg("attempting socket read in get_input..."); + res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen); + pthread_mutex_unlock(&s->lock); + if (res < 1) + return -1; + } + s->inlen += res; + s->inbuf[s->inlen] = '\0'; + return 0; + /* We have some input, but it's not ready for processing */ +} + +char *astman_get_header(struct message *m, char *var) +{ + char cmp[80]; + int x; + snprintf(cmp, sizeof(cmp), "%s: ", var); + for (x=0;xhdrcount;x++) + if (!strncasecmp(cmp, m->headers[x], strlen(cmp))) + return m->headers[x] + strlen(cmp); + return ""; +} + +int AddHeader(struct message *m, const char *fmt, ...) { + va_list ap; + + int res; + + if (m->hdrcount < MAX_HEADERS - 1) { + va_start(ap, fmt); + vsprintf(m->headers[m->hdrcount], fmt, ap); + va_end(ap); + m->hdrcount++; + res = 0; + } else + res = 1; + + return res; +} + +/* Recursive thread safe replacement of inet_ntoa */ +const char *ast_inet_ntoa(char *buf, int bufsiz, struct in_addr ia) +{ + return inet_ntop(AF_INET, &ia, buf, bufsiz); +} + + +int connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec) +{ + int flags, n, error; + socklen_t len; + fd_set rset, wset; + struct timeval tval; + + flags = fcntl(sockfd, F_GETFL, 0); + fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + + error = 0; + if ( (n = connect(sockfd, (struct sockaddr *) saptr, salen)) < 0) + if (errno != EINPROGRESS) + return(-1); + + /* Do whatever we want while the connect is taking place. */ + + if (n == 0) + goto done; /* connect completed immediately */ + + FD_ZERO(&rset); + FD_SET(sockfd, &rset); + wset = rset; + tval.tv_sec = nsec; + tval.tv_usec = 0; + + if ( (n = select(sockfd+1, &rset, &wset, NULL, + nsec ? &tval : NULL)) == 0) { + /*close(sockfd);*/ /* we want to retry */ + errno = ETIMEDOUT; + return(-1); + } + + if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { + len = sizeof(error); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) + return(-1); /* Solaris pending error */ + } else { + /*err_quit("select error: sockfd not set");*/ + logmsg("select error: sockfd not set"); + return(-1); + } + +done: + fcntl(sockfd, F_SETFL, flags); /* restore file status flags */ + + if (error) { + /* close(sockfd); */ /* disable for now, we want to retry... */ + errno = error; + return(-1); + } + return(0); +} diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..bfb8d1f --- /dev/null +++ b/src/config.c @@ -0,0 +1,280 @@ +#include +#include +#include "astmanproxy.h" + +extern struct iohandler *iohandlers; + +void *add_server(char *srvspec) { + + int ccount = 0; + struct ast_server *srv; + char *s; + + /* malloc ourselves a server credentials structure */ + srv = malloc(sizeof(struct ast_server)); + if ( !srv ) { + fprintf(stderr, "Failed to allocate server credentials: %s\n", strerror(errno)); + exit(1); + } + memset(srv, 0, sizeof (struct ast_server) ); + + s = srvspec; + do { + *s = tolower(*s); + if ( *s == ',' ) { + ccount++; + continue; + } + switch(ccount) { + case 0: + strncat(srv->ast_host, s, 1); + break; + case 1: + strncat(srv->ast_port, s, 1); + break; + case 2: + strncat(srv->ast_user, s, 1); + break; + case 3: + strncat(srv->ast_pass, s, 1); + break; + case 4: + strncat(srv->ast_events, s, 1); + break; + } + } while (*(s++)); + + if (!srv->ast_host || !srv->ast_port || !srv->ast_user || !srv->ast_pass || !srv->ast_events) { + fprintf(stderr, "Aborting! Server spec incomplete: %s!\n", srvspec); + free(srv); + exit(1); + } else { + srv->next = pc.serverlist; + pc.serverlist = srv; + } + + return 0; + /* TODO: make sure server credentials are freed at leave */ +} + +void *processline(char *s) { + char name[80],value[80]; + int nvstate = 0; + + + memset (name,0,sizeof name); + memset (value,0,sizeof value); + + do { + *s = tolower(*s); + + if ( *s == ' ' || *s == '\t') + continue; + if ( *s == ';' || *s == '#' || *s == '\r' || *s == '\n' ) + break; + if ( *s == '=' ) { + nvstate = 1; + continue; + } + if (!nvstate) + strncat(name, s, 1); + else + strncat(value, s, 1); + } while (*(s++)); + + if (debug) + debugmsg("config: %s, %s", name, value); + + if ( !strcmp(name,"host") ) + add_server(value); + else if (!strcmp(name,"retryinterval") ) + pc.retryinterval = atoi(value); + else if (!strcmp(name,"maxretries") ) + pc.maxretries = atoi(value); + else if (!strcmp(name,"listenaddress") ) + strcpy(pc.listen_addr, value); + else if (!strcmp(name,"listenport") ) + pc.listen_port = atoi(value); + else if (!strcmp(name,"proxykey") ) + strcpy(pc.key, value); + else if (!strcmp(name,"proc_user") ) + strcpy(pc.proc_user, value); + else if (!strcmp(name,"proc_group") ) + strcpy(pc.proc_group, value); + else if (!strcmp(name,"logfile") ) + strcpy(pc.logfile, value); + else if (!strcmp(name,"autofilter") ) + pc.autofilter = strcmp(value,"on") ? 0 : 1; + else if (!strcmp(name,"outputformat") ) + strcpy(pc.outputformat, value); + else if (!strcmp(name,"inputformat") ) + strcpy(pc.inputformat, value); + + return 0; +} + +int LoadHandlers() { + + void *dlhandle = NULL; + const char *error; + char fmt[20], moddir[80] = MDIR, modfile[80]; + DIR *mods; + struct dirent *d; + void *rh, *wh, *och; + struct iohandler *io = NULL; + + mods = opendir(moddir); + if (!mods) + exit(1); + + while((d = readdir(mods))) { + /* Must end in .so to load it. */ + if ( (strlen(d->d_name) > 3) && !strcasecmp(d->d_name + strlen(d->d_name) - 3, ".so") ) { + + memset(fmt, 0, sizeof fmt); + strncpy(fmt, d->d_name, strlen(d->d_name) - 3); + + sprintf(modfile, "%s/%s", moddir, d->d_name); + if (debug) + debugmsg("loading: module %s (%s)", fmt, modfile); + + dlhandle = dlopen (modfile, RTLD_LAZY); + if (!dlhandle) { + fprintf(stderr, "dlopen failed: %s\n", dlerror()); + exit(1); + } + + rh = dlsym(dlhandle, "_read"); + if ((error = dlerror()) != NULL) { + if (debug) + debugmsg("loading: note, %s_read does not exist; ignoring", fmt); + } + + wh = dlsym(dlhandle, "_write"); + if ((error = dlerror()) != NULL) { + if (debug) + debugmsg("loading: note, %s_write does not exist; ignoring", fmt); + } + + och = dlsym(dlhandle, "_onconnect"); + if ((error = dlerror()) != NULL) { + if (debug) + debugmsg("loading: note, %s_onconnect does not exist; ignoring", fmt); + } + + if (rh || wh) { + io = malloc(sizeof(struct iohandler)); + memset(io, 0, sizeof(struct iohandler)); + strcpy(io->formatname, fmt); + if (rh) + io->read = rh; + if (wh) + io->write = wh; + if (och) + io->onconnect = och; + io->autodisconnect = dlsym(dlhandle, "_autodisconnect"); + if ((error = dlerror()) != NULL) { + if (debug) + debugmsg("loading: note, %s_autodisconnect not defined; ignoring", fmt); + } + io->dlhandle = dlhandle; + io->next = iohandlers; + iohandlers = io; + } else + dlclose(dlhandle); + + } + } + closedir(mods); + + if (!iohandlers) { + fprintf(stderr, "Unable to load *ANY* IO Handlers from %s!\n", MDIR); + exit(1); + } + + return 0; +} + + +int ReadConfig() { + FILE *FP; + char buf[1024]; + char cfn[80]; + + + memset( &pc, 0, sizeof pc ); + sprintf(cfn, "%s/%s", CDIR, CFILE); + FP = fopen( cfn, "r" ); + + if ( !FP ) + { + fprintf(stderr, "Unable to open config file: %s/%s!\n", CDIR, CFILE); + exit( 1 ); + } + + if (debug) + debugmsg("config: parsing configuration file: %s", cfn); + + while ( fgets( buf, sizeof buf, FP ) ) { + if (*buf == ';' || *buf == '\r' || *buf == '\n' || *buf == '#') continue; + processline(buf); + } + + fclose(FP); + + return 0; +} + +FILE *OpenLogfile() { + FILE *FP; + FP = fopen( pc.logfile, "a" ); + if ( !FP ) { + fprintf(stderr, "Unable to open logfile: %s!\n", pc.logfile); + exit( 1 ); + } + + return FP; +} + +int SetProcUID() { + + struct passwd *pwent; + struct group *gp; + uid_t newuid = 0; + gid_t newgid = 0; + + if ((pwent = (struct passwd *)getpwnam( pc.proc_user )) == NULL) { + fprintf(stderr, "getpwnam(%s) failed.\n", pc.proc_user); + return(-1); + } else + newuid = pwent->pw_uid; + + if ( newuid == 0 ) { + fprintf(stderr, "getpwnam(%s) returned root user; aborting!\n", pc.proc_user); + return(-1); + } + + if ((gp = (struct group *)getgrnam( pc.proc_group )) == NULL) { + fprintf(stderr, "getgrnam(%s) failed.\n", pc.proc_group); + return(-1); + } else + newgid = gp->gr_gid; + + if ( chown( pc.logfile, newuid, newgid ) < 0 ) + { + fprintf(stderr, "chown(%d,%d) of %s failed!\n", newuid, newgid, pc.logfile); + return( -1 ); + } + + if (setgid(newgid) < 0) { + fprintf(stderr, "setgid(%d) failed.\n", newgid); + return(-1); + } + + if (setuid(newuid) < 0) { + fprintf(stderr, "setuid(%d) failed.\n", newuid); + return(-1); + } + + return 0; +} diff --git a/src/config_perms.c b/src/config_perms.c new file mode 100644 index 0000000..939d90f --- /dev/null +++ b/src/config_perms.c @@ -0,0 +1,127 @@ +/* #include */ +/* #include */ +#include "astmanproxy.h" + +extern pthread_mutex_t userslock; + +void *free_userperm(struct proxy_user *pu) { + struct proxy_user *next_pu; + + while( pu ) { + next_pu = pu->next; + free( pu ); + pu = next_pu; + } + return 0; +} + +void *add_userperm(char* username, char *userspec, struct proxy_user **pu) { + + int ccount = 0; + struct proxy_user *user; + char *s; + + /* malloc ourselves a server credentials structure */ + user = malloc(sizeof(struct proxy_user)); + if ( !user ) { + fprintf(stderr, "Failed to allocate user credentials: %s\n", strerror(errno)); + exit(1); + } + memset(user, 0, sizeof (struct proxy_user) ); + + s = userspec; + strncpy(user->username, username, sizeof(user->username)-1 ); + do { + if ( *s == ',' ) { + ccount++; + continue; + } + switch(ccount) { + case 0: + strncat(user->secret, s, 1); + break; + case 1: + strncat(user->channel, s, 1); + break; + case 2: + strncat(user->ocontext, s, 1); + break; + case 3: + strncat(user->icontext, s, 1); + break; + } + } while (*(s++)); + + user->next = *pu; + *pu = user; + + return 0; +} + +void *processperm(char *s, struct proxy_user **pu) { + char name[80],value[80]; + int nvstate = 0; + + + memset (name,0,sizeof name); + memset (value,0,sizeof value); + + do { + *s = tolower(*s); + + if ( *s == ' ' || *s == '\t') + continue; + if ( *s == ';' || *s == '#' || *s == '\r' || *s == '\n' ) + break; + if ( *s == '=' ) { + nvstate = 1; + continue; + } + if (!nvstate) + strncat(name, s, 1); + else + strncat(value, s, 1); + } while (*(s++)); + + if (debug) + debugmsg("perm: %s, %s", name, value); + + add_userperm(name,value,pu); + + return 0; +} + +int ReadPerms() { + FILE *FP; + char buf[1024]; + char cfn[80]; + struct proxy_user *pu; + + pu=0; + sprintf(cfn, "%s/%s", PDIR, PFILE); + FP = fopen( cfn, "r" ); + + if ( !FP ) + { + fprintf(stderr, "Unable to open permissions file: %s/%s!\n", PDIR, PFILE); + exit( 1 ); + } + + if (debug) + debugmsg("config: parsing configuration file: %s", cfn); + + while ( fgets( buf, sizeof buf, FP ) ) { + if (*buf == ';' || *buf == '\r' || *buf == '\n' || *buf == '#') continue; + processperm(buf,&pu); + } + + fclose(FP); + + pthread_mutex_lock(&userslock); + free_userperm(pc.userlist); + pc.userlist=pu; + pthread_mutex_unlock(&userslock); + + return 0; +} + diff --git a/src/csv.c b/src/csv.c new file mode 100644 index 0000000..bf8e800 --- /dev/null +++ b/src/csv.c @@ -0,0 +1,33 @@ +/* Asterisk Manager Proxy + Copyright (c) 2005 David C. Troy + + This program is free software, distributed under the terms of + the GNU General Public License. + + CSV I/O Handler +*/ + +#include "astmanproxy.h" + +/* TODO: catch and expand/handle commas in output */ + +int _write(struct mansession *s, struct message *m) { + int i; + char outstring[MAX_LEN]; + + pthread_mutex_lock(&s->lock); + for (i=0; ihdrcount; i++) { + sprintf(outstring, "\"%s\"", m->headers[i]); + if (ihdrcount-1) + strcat(outstring, ", "); + write(s->fd, outstring, strlen(outstring)); + } + write(s->fd, "\r\n\r\n", 4); + pthread_mutex_unlock(&s->lock); + + return 0; +} + +int _autodisconnect() { + return 0; +} diff --git a/src/dlfcn.c b/src/dlfcn.c new file mode 100644 index 0000000..98d2373 --- /dev/null +++ b/src/dlfcn.c @@ -0,0 +1,1282 @@ +/* +Copyright (c) 2002 Jorge Acereda & + Peter O'Gorman + +Portions may be copyright others, see the AUTHORS file included with this +distribution. + +Maintained by Peter O'Gorman + +Bug Reports and other queries should go to + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* Just playing to see if it would compile with the freebsd headers, it does, + * but because of the different values for RTLD_LOCAL etc, it would break binary + * compat... oh well + */ +#ifndef __BSD_VISIBLE +#define __BSD_VISIBLE 1 +#endif +#include "dlfcn-compat.h" + +#ifndef dl_restrict +#define dl_restrict __restrict +#endif +/* This is not available on 10.1 */ +#ifndef LC_LOAD_WEAK_DYLIB +#define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD) +#endif + +/* With this stuff here, this thing may actually compile/run on 10.0 systems + * Not that I have a 10.0 system to test it on anylonger + */ +#ifndef LC_REQ_DYLD +#define LC_REQ_DYLD 0x80000000 +#endif +#ifndef NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED +#define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4 +#endif +#ifndef NSADDIMAGE_OPTION_RETURN_ON_ERROR +#define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 +#endif +#ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_BIND +#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0 +#endif +#ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR +#define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4 +#endif +/* These symbols will be looked for in dyld */ +static const struct mach_header *(*dyld_NSAddImage) (const char *, unsigned long) = 0; +static int (*dyld_NSIsSymbolNameDefinedInImage) (const struct mach_header *, const char *) = 0; +static NSSymbol(*dyld_NSLookupSymbolInImage) + (const struct mach_header *, const char *, unsigned long) = 0; + +/* Define this to make dlcompat reuse data block. This way in theory we save + * a little bit of overhead. However we then couldn't correctly catch excess + * calls to dlclose(). Hence we don't use this feature + */ +#undef REUSE_STATUS + +/* Size of the internal error message buffer (used by dlerror()) */ +#define ERR_STR_LEN 251 + +/* Maximum number of search paths supported by getSearchPath */ +#define MAX_SEARCH_PATHS 32 + + +#define MAGIC_DYLIB_OFI ((NSObjectFileImage) 'DYOF') +#define MAGIC_DYLIB_MOD ((NSModule) 'DYMO') + +/* internal flags */ +#define DL_IN_LIST 0x01 + +/* our mutex */ +static pthread_mutex_t dlcompat_mutex; +/* Our thread specific storage + */ +static pthread_key_t dlerror_key; + +struct dlthread +{ + int lockcnt; + unsigned char errset; + char errstr[ERR_STR_LEN]; +}; + +/* This is our central data structure. Whenever a module is loaded via + * dlopen(), we create such a struct. + */ +struct dlstatus +{ + struct dlstatus *next; /* pointer to next element in the linked list */ + NSModule module; + const struct mach_header *lib; + int refs; /* reference count */ + int mode; /* mode in which this module was loaded */ + dev_t device; + ino_t inode; + int flags; /* Any internal flags we may need */ +}; + +/* Head node of the dlstatus list */ +static struct dlstatus mainStatus = { 0, MAGIC_DYLIB_MOD, NULL, -1, RTLD_GLOBAL, 0, 0, 0 }; +static struct dlstatus *stqueue = &mainStatus; + + +/* Storage for the last error message (used by dlerror()) */ +/* static char err_str[ERR_STR_LEN]; */ +/* static int err_filled = 0; */ + +/* Prototypes to internal functions */ +static void debug(const char *fmt, ...); +static void error(const char *str, ...); +static const char *safegetenv(const char *s); +static const char *searchList(void); +static const char *getSearchPath(int i); +static const char *getFullPath(int i, const char *file); +static const struct stat *findFile(const char *file, const char **fullPath); +static int isValidStatus(struct dlstatus *status); +static inline int isFlagSet(int mode, int flag); +static struct dlstatus *lookupStatus(const struct stat *sbuf); +static void insertStatus(struct dlstatus *dls, const struct stat *sbuf); +static int promoteLocalToGlobal(struct dlstatus *dls); +static void *reference(struct dlstatus *dls, int mode); +static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError); +static struct dlstatus *allocStatus(void); +static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode); +static NSSymbol *search_linked_libs(const struct mach_header *mh, const char *symbol); +static const char *get_lib_name(const struct mach_header *mh); +static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod); +static void dlcompat_init_func(void); +static inline void dlcompat_init_check(void); +static inline void dolock(void); +static inline void dounlock(void); +static void dlerrorfree(void *data); +static void resetdlerror(void); +static const struct mach_header *my_find_image(const char *name); +static const struct mach_header *image_for_address(const void *address); +static inline const char *dyld_error_str(void); + +#if FINK_BUILD +/* Two Global Functions */ +void *dlsym_prepend_underscore(void *handle, const char *symbol); +void *dlsym_auto_underscore(void *handle, const char *symbol); + +/* And their _intern counterparts */ +static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol); +static void *dlsym_auto_underscore_intern(void *handle, const char *symbol); +#endif + +/* Functions */ + +static void debug(const char *fmt, ...) +{ +#if DEBUG > 1 + va_list arg; + va_start(arg, fmt); + fprintf(stderr, "DLDEBUG: "); + vfprintf(stderr, fmt, arg); + fprintf(stderr, "\n"); + fflush(stderr); + va_end(arg); +#endif +} + +static void error(const char *str, ...) +{ + va_list arg; + struct dlthread *tss; + char * err_str; + va_start(arg, str); + tss = pthread_getspecific(dlerror_key); + err_str = tss->errstr; + strncpy(err_str, "dlcompat: ", ERR_STR_LEN); + vsnprintf(err_str + 10, ERR_STR_LEN - 10, str, arg); + va_end(arg); + debug("ERROR: %s\n", err_str); + tss->errset = 1; +} + +static void warning(const char *str) +{ +#if DEBUG > 0 + fprintf(stderr, "WARNING: dlcompat: %s\n", str); +#endif +} + +static const char *safegetenv(const char *s) +{ + const char *ss = getenv(s); + return ss ? ss : ""; +} + +/* because this is only used for debugging and error reporting functions, we + * don't really care about how elegant it is... it could use the load + * commands to find the install name of the library, but... + */ +static const char *get_lib_name(const struct mach_header *mh) +{ + unsigned long count = _dyld_image_count(); + unsigned long i; + const char *val = NULL; + if (mh) + { + for (i = 0; i < count; i++) + { + if (mh == _dyld_get_image_header(i)) + { + val = _dyld_get_image_name(i); + break; + } + } + } + return val; +} + +/* Returns the mach_header for the module bu going through all the loaded images + * and finding the one with the same name as the module. There really ought to be + * an api for doing this, would be faster, but there isn't one right now + */ +static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod) +{ + const char *mod_name = NSNameOfModule(mod); + struct mach_header *mh = NULL; + unsigned long count = _dyld_image_count(); + unsigned long i; + debug("Module name: %s", mod_name); + for (i = 0; i < count; i++) + { + if (!strcmp(mod_name, _dyld_get_image_name(i))) + { + mh = _dyld_get_image_header(i); + break; + } + } + return mh; +} + + +/* Compute and return a list of all directories that we should search when + * trying to locate a module. We first look at the values of LD_LIBRARY_PATH + * and DYLD_LIBRARY_PATH, and then finally fall back to looking into + * /usr/lib and /lib. Since both of the environments variables can contain a + * list of colon seperated paths, we simply concat them and the two other paths + * into one big string, which we then can easily parse. + * Splitting this string into the actual path list is done by getSearchPath() + */ +static const char *searchList() +{ + size_t buf_size; + static char *buf=NULL; + const char *ldlp = safegetenv("LD_LIBRARY_PATH"); + const char *dyldlp = safegetenv("DYLD_LIBRARY_PATH"); + const char *stdpath = getenv("DYLD_FALLBACK_LIBRARY_PATH"); + if (!stdpath) + stdpath = "/usr/local/lib:/lib:/usr/lib"; + if (!buf) + { + buf_size = strlen(ldlp) + strlen(dyldlp) + strlen(stdpath) + 4; + buf = malloc(buf_size); + snprintf(buf, buf_size, "%s%s%s%s%s%c", dyldlp, (dyldlp[0] ? ":" : ""), ldlp, (ldlp[0] ? ":" : ""), + stdpath, '\0'); + } + return buf; +} + +/* Returns the ith search path from the list as computed by searchList() */ +static const char *getSearchPath(int i) +{ + static const char *list = 0; + static char **path = (char **)0; + static int end = 0; + static int numsize = MAX_SEARCH_PATHS; + static char **tmp; + /* So we can call free() in the "destructor" we use i=-1 to return the alloc'd array */ + if (i == -1) + { + return (const char*)path; + } + if (!path) + { + path = (char **)calloc(MAX_SEARCH_PATHS, sizeof(char **)); + } + if (!list && !end) + list = searchList(); + if (i >= (numsize)) + { + debug("Increasing size for long PATH"); + tmp = (char **)calloc((MAX_SEARCH_PATHS + numsize), sizeof(char **)); + if (tmp) + { + memcpy(tmp, path, sizeof(char **) * numsize); + free(path); + path = tmp; + numsize += MAX_SEARCH_PATHS; + } + else + { + return 0; + } + } + + while (!path[i] && !end) + { + path[i] = strsep((char **)&list, ":"); + + if (path[i][0] == 0) + path[i] = 0; + end = (list == 0); + } + return path[i]; +} + +static const char *getFullPath(int i, const char *file) +{ + static char buf[PATH_MAX]; + const char *path = getSearchPath(i); + if (path) + { + snprintf(buf, PATH_MAX, "%s/%s", path, file); + } + return path ? buf : 0; +} + +/* Given a file name, try to determine the full path for that file. Starts + * its search in the current directory, and then tries all paths in the + * search list in the order they are specified there. + */ +static const struct stat *findFile(const char *file, const char **fullPath) +{ + int i = 0; + static struct stat sbuf; + char *fileName; + debug("finding file %s", file); + *fullPath = file; + if (0 == stat(file, &sbuf)) + return &sbuf; + if (strchr(file, '/')) + return 0; /* If the path had a / we don't look in env var places */ + fileName = NULL; + if (!fileName) + fileName = (char *)file; + while ((*fullPath = getFullPath(i++, fileName))) + { + if (0 == stat(*fullPath, &sbuf)) + return &sbuf; + } + ; + return 0; +} + +/* Determine whether a given dlstatus is valid or not */ +static int isValidStatus(struct dlstatus *status) +{ + /* Walk the list to verify status is contained in it */ + struct dlstatus *dls = stqueue; + while (dls && status != dls) + dls = dls->next; + if (dls == 0) + error("invalid handle"); + else if ((dls->module == 0) || (dls->refs == 0)) + error("handle to closed library"); + else + return TRUE; + return FALSE; +} + +static inline int isFlagSet(int mode, int flag) +{ + return (mode & flag) == flag; +} + +static struct dlstatus *lookupStatus(const struct stat *sbuf) +{ + struct dlstatus *dls = stqueue; + debug("looking for status"); + while (dls && ( /* isFlagSet(dls->mode, RTLD_UNSHARED) */ 0 + || sbuf->st_dev != dls->device || sbuf->st_ino != dls->inode)) + dls = dls->next; + return dls; +} + +static void insertStatus(struct dlstatus *dls, const struct stat *sbuf) +{ + debug("inserting status"); + dls->inode = sbuf->st_ino; + dls->device = sbuf->st_dev; + dls->refs = 0; + dls->mode = 0; + if ((dls->flags & DL_IN_LIST) == 0) + { + dls->next = stqueue; + stqueue = dls; + dls->flags |= DL_IN_LIST; + } +} + +static struct dlstatus *allocStatus() +{ + struct dlstatus *dls; +#ifdef REUSE_STATUS + dls = stqueue; + while (dls && dls->module) + dls = dls->next; + if (!dls) +#endif + dls = calloc(sizeof(*dls),1); + return dls; +} + +static int promoteLocalToGlobal(struct dlstatus *dls) +{ + static int (*p) (NSModule module) = 0; + debug("promoting"); + if (!p) + _dyld_func_lookup("__dyld_NSMakePrivateModulePublic", (unsigned long *)&p); + return (dls->module == MAGIC_DYLIB_MOD) || (p && p(dls->module)); +} + +static void *reference(struct dlstatus *dls, int mode) +{ + if (dls) + { + if (dls->module == MAGIC_DYLIB_MOD && isFlagSet(mode, RTLD_LOCAL)) + { + warning("trying to open a .dylib with RTLD_LOCAL"); + error("unable to open a .dylib with RTLD_LOCAL"); + return NULL; + } + if (isFlagSet(mode, RTLD_GLOBAL) && + !isFlagSet(dls->mode, RTLD_GLOBAL) && !promoteLocalToGlobal(dls)) + { + error("unable to promote local module to global"); + return NULL; + } + dls->mode |= mode; + dls->refs++; + } + else + debug("reference called with NULL argument"); + + return dls; +} + +static const struct mach_header *my_find_image(const char *name) +{ + const struct mach_header *mh = 0; + const char *id = NULL; + int i = _dyld_image_count(); + int j; + mh = (struct mach_header *) + dyld_NSAddImage(name, NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED | + NSADDIMAGE_OPTION_RETURN_ON_ERROR); + if (!mh) + { + for (j = 0; j < i; j++) + { + id = _dyld_get_image_name(j); + if (!strcmp(id, name)) + { + mh = _dyld_get_image_header(j); + break; + } + } + } + return mh; +} + +/* + * dyld adds libraries by first adding the directly dependant libraries in link order, and + * then adding the dependencies for those libraries, so we should do the same... but we don't + * bother adding the extra dependencies, if the symbols are neither in the loaded image nor + * any of it's direct dependencies, then it probably isn't there. + */ +NSSymbol *search_linked_libs(const struct mach_header * mh, const char *symbol) +{ + unsigned int n; + struct load_command *lc = 0; + struct mach_header *wh; + NSSymbol *nssym = 0; + if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) + { + lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); + for (n = 0; n < mh->ncmds; n++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) + { + if ((LC_LOAD_DYLIB == lc->cmd) || (LC_LOAD_WEAK_DYLIB == lc->cmd)) + { + if ((wh = (struct mach_header *) + my_find_image((char *)(((struct dylib_command *)lc)->dylib.name.offset + + (char *)lc)))) + { + if (dyld_NSIsSymbolNameDefinedInImage(wh, symbol)) + { + nssym = dyld_NSLookupSymbolInImage(wh, + symbol, + NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | + NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); + break; + } + } + } + } + if ((!nssym) && NSIsSymbolNameDefined(symbol)) + { + /* I've never seen this debug message...*/ + debug("Symbol \"%s\" is defined but was not found", symbol); + } + } + return nssym; +} + +/* Up to the caller to free() returned string */ +static inline const char *dyld_error_str() +{ + NSLinkEditErrors dylder; + int dylderno; + const char *dylderrstr; + const char *dyldfile; + const char* retStr = NULL; + NSLinkEditError(&dylder, &dylderno, &dyldfile, &dylderrstr); + if (dylderrstr && strlen(dylderrstr)) + { + retStr = malloc(strlen(dylderrstr) +1); + strcpy((char*)retStr,dylderrstr); + } + return retStr; +} + +static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError) +{ + NSSymbol *nssym = 0; +#ifdef __GCC__ + void *caller = __builtin_return_address(1); /* Be *very* careful about inlining */ +#else + void *caller = NULL; +#endif + const struct mach_header *caller_mh = 0; + const char* savedErrorStr = NULL; + resetdlerror(); +#ifndef RTLD_SELF +#define RTLD_SELF ((void *) -3) +#endif + if (NULL == dls) + dls = RTLD_SELF; + if ((RTLD_NEXT == dls) || (RTLD_SELF == dls)) + { + if (dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage && caller) + { + caller_mh = image_for_address(caller); + if (RTLD_SELF == dls) + { + /* FIXME: We should be using the NSModule api, if SELF is an MH_BUNDLE + * But it appears to work anyway, and looking at the code in dyld_libfuncs.c + * this is acceptable. + */ + if (dyld_NSIsSymbolNameDefinedInImage(caller_mh, symbol)) + { + nssym = dyld_NSLookupSymbolInImage(caller_mh, + symbol, + NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | + NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); + } + } + if (!nssym) + { + if (RTLD_SELF == dls) + savedErrorStr = dyld_error_str(); + nssym = search_linked_libs(caller_mh, symbol); + } + } + else + { + if (canSetError) + error("RTLD_SELF and RTLD_NEXT are not supported"); + return NULL; + } + } + if (!nssym) + { + + if (RTLD_DEFAULT == dls) + { + dls = &mainStatus; + } + if (!isValidStatus(dls)) + return NULL; + + if (dls->module != MAGIC_DYLIB_MOD) + { + nssym = NSLookupSymbolInModule(dls->module, symbol); + if (!nssym && NSIsSymbolNameDefined(symbol)) + { + debug("Searching dependencies"); + savedErrorStr = dyld_error_str(); + nssym = search_linked_libs(get_mach_header_from_NSModule(dls->module), symbol); + } + } + else if (dls->lib && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) + { + if (dyld_NSIsSymbolNameDefinedInImage(dls->lib, symbol)) + { + nssym = dyld_NSLookupSymbolInImage(dls->lib, + symbol, + NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | + NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); + } + else if (NSIsSymbolNameDefined(symbol)) + { + debug("Searching dependencies"); + savedErrorStr = dyld_error_str(); + nssym = search_linked_libs(dls->lib, symbol); + } + } + else if (dls->module == MAGIC_DYLIB_MOD) + { + /* Global context, use NSLookupAndBindSymbol */ + if (NSIsSymbolNameDefined(symbol)) + { + /* There doesn't seem to be a return on error option for this call??? + this is potentially broken, if binding fails, it will improperly + exit the application. */ + nssym = NSLookupAndBindSymbol(symbol); + } + else + { + if (savedErrorStr) + free((char*)savedErrorStr); + savedErrorStr = malloc(256); + snprintf((char*)savedErrorStr, 256, "Symbol \"%s\" not in global context",symbol); + } + } + } + /* Error reporting */ + if (!nssym) + { + if (!savedErrorStr || !strlen(savedErrorStr)) + { + if (savedErrorStr) + free((char*)savedErrorStr); + savedErrorStr = malloc(256); + snprintf((char*)savedErrorStr, 256,"Symbol \"%s\" not found",symbol); + } + if (canSetError) + { + error(savedErrorStr); + } + else + { + debug(savedErrorStr); + } + if (savedErrorStr) + free((char*)savedErrorStr); + return NULL; + } + return NSAddressOfSymbol(nssym); +} + +static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode) +{ + NSObjectFileImage ofi = 0; + NSObjectFileImageReturnCode ofirc; + struct dlstatus *dls; + NSLinkEditErrors ler; + int lerno; + const char *errstr; + const char *file; + void (*init) (void); + ofirc = NSCreateObjectFileImageFromFile(path, &ofi); + switch (ofirc) + { + case NSObjectFileImageSuccess: + break; + case NSObjectFileImageInappropriateFile: + if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) + { + if (isFlagSet(mode, RTLD_LOCAL)) + { + warning("trying to open a .dylib with RTLD_LOCAL"); + error("unable to open this file with RTLD_LOCAL"); + return NULL; + } + } + else + { + error("opening this file is unsupported on this system"); + return NULL; + } + break; + case NSObjectFileImageFailure: + error("object file setup failure"); + return NULL; + case NSObjectFileImageArch: + error("no object for this architecture"); + return NULL; + case NSObjectFileImageFormat: + error("bad object file format"); + return NULL; + case NSObjectFileImageAccess: + error("can't read object file"); + return NULL; + default: + error("unknown error from NSCreateObjectFileImageFromFile()"); + return NULL; + } + dls = lookupStatus(sbuf); + if (!dls) + { + dls = allocStatus(); + } + if (!dls) + { + error("unable to allocate memory"); + return NULL; + } + // dls->lib = 0; + if (ofirc == NSObjectFileImageInappropriateFile) + { + if ((dls->lib = dyld_NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR))) + { + debug("Dynamic lib loaded at %ld", dls->lib); + ofi = MAGIC_DYLIB_OFI; + dls->module = MAGIC_DYLIB_MOD; + ofirc = NSObjectFileImageSuccess; + /* Although it is possible with a bit of work to modify this so it works and + functions with RTLD_NOW, I don't deem it necessary at the moment */ + } + if (!(dls->module)) + { + NSLinkEditError(&ler, &lerno, &file, &errstr); + if (!errstr || (!strlen(errstr))) + error("Can't open this file type"); + else + error(errstr); + if ((dls->flags & DL_IN_LIST) == 0) + { + free(dls); + } + return NULL; + } + } + else + { + dls->module = NSLinkModule(ofi, path, + NSLINKMODULE_OPTION_RETURN_ON_ERROR | + NSLINKMODULE_OPTION_PRIVATE | + (isFlagSet(mode, RTLD_NOW) ? NSLINKMODULE_OPTION_BINDNOW : 0)); + NSDestroyObjectFileImage(ofi); + if (dls->module) + { + dls->lib = get_mach_header_from_NSModule(dls->module); + } + } + if (!dls->module) + { + NSLinkEditError(&ler, &lerno, &file, &errstr); + if ((dls->flags & DL_IN_LIST) == 0) + { + free(dls); + } + error(errstr); + return NULL; + } + + insertStatus(dls, sbuf); + dls = reference(dls, mode); + if ((init = dlsymIntern(dls, "__init", 0))) + { + debug("calling _init()"); + init(); + } + return dls; +} + +inline static void dlcompat_init_check(void) +{ + static pthread_mutex_t l = PTHREAD_MUTEX_INITIALIZER; + static int init_done = 0; + + pthread_mutex_lock(&l); + if (!init_done) { + dlcompat_init_func(); + init_done = 1; + } + pthread_mutex_unlock(&l); +} + +static void dlcompat_init_func(void) +{ + _dyld_func_lookup("__dyld_NSAddImage", (unsigned long *)&dyld_NSAddImage); + _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage", + (unsigned long *)&dyld_NSIsSymbolNameDefinedInImage); + _dyld_func_lookup("__dyld_NSLookupSymbolInImage", (unsigned long *)&dyld_NSLookupSymbolInImage); + if (pthread_mutex_init(&dlcompat_mutex, NULL)) + exit(1); + if (pthread_key_create(&dlerror_key, &dlerrorfree)) + exit(1); +} + +static void resetdlerror() +{ + struct dlthread *tss; + tss = pthread_getspecific(dlerror_key); + tss->errset = 0; +} + +static void dlerrorfree(void *data) +{ + free(data); +} + +/* We kind of want a recursive lock here, but meet a little trouble + * because they are not available pre OS X 10.2, so we fake it + * using thread specific storage to keep a lock count + */ +static inline void dolock(void) +{ + int err = 0; + struct dlthread *tss; + dlcompat_init_check(); + tss = pthread_getspecific(dlerror_key); + if (!tss) + { + tss = malloc(sizeof(struct dlthread)); + tss->lockcnt = 0; + tss->errset = 0; + if (pthread_setspecific(dlerror_key, tss)) + { + fprintf(stderr,"dlcompat: pthread_setspecific failed\n"); + exit(1); + } + } + if (!tss->lockcnt) + err = pthread_mutex_lock(&dlcompat_mutex); + tss->lockcnt = tss->lockcnt +1; + if (err) + exit(err); +} + +static inline void dounlock(void) +{ + int err = 0; + struct dlthread *tss; + tss = pthread_getspecific(dlerror_key); + tss->lockcnt = tss->lockcnt -1; + if (!tss->lockcnt) + err = pthread_mutex_unlock(&dlcompat_mutex); + if (err) + exit(err); +} + +void *dlopen(const char *path, int mode) +{ + const struct stat *sbuf; + struct dlstatus *dls; + const char *fullPath; + + dolock(); + resetdlerror(); + if (!path) + { + dls = &mainStatus; + goto dlopenok; + } + if (!(sbuf = findFile(path, &fullPath))) + { + error("file \"%s\" not found", path); + goto dlopenerror; + } + /* Now checks that it hasn't been closed already */ + if ((dls = lookupStatus(sbuf)) && (dls->refs > 0)) + { + debug("status found"); + dls = reference(dls, mode); + goto dlopenok; + } +#ifdef RTLD_NOLOAD + if (isFlagSet(mode, RTLD_NOLOAD)) + { + error("no existing handle and RTLD_NOLOAD specified"); + goto dlopenerror; + } +#endif + if (isFlagSet(mode, RTLD_LAZY) && isFlagSet(mode, RTLD_NOW)) + { + error("how can I load something both RTLD_LAZY and RTLD_NOW?"); + goto dlopenerror; + } + dls = loadModule(fullPath, sbuf, mode); + + dlopenok: + dounlock(); + return (void *)dls; + dlopenerror: + dounlock(); + return NULL; +} + +#if !FINK_BUILD +void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol) +{ + int sym_len = strlen(symbol); + void *value = NULL; + char *malloc_sym = NULL; + dolock(); + malloc_sym = malloc(sym_len + 2); + if (malloc_sym) + { + sprintf(malloc_sym, "_%s", symbol); + value = dlsymIntern(handle, malloc_sym, 1); + free(malloc_sym); + } + else + { + error("Unable to allocate memory"); + goto dlsymerror; + } + dounlock(); + return value; + dlsymerror: + dounlock(); + return NULL; +} +#endif + +#if FINK_BUILD + +void *dlsym_prepend_underscore(void *handle, const char *symbol) +{ + void *answer; + dolock(); + answer = dlsym_prepend_underscore_intern(handle, symbol); + dounlock(); + return answer; +} + +static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol) +{ +/* + * A quick and easy way for porting packages which call dlsym(handle,"sym") + * If the porter adds -Ddlsym=dlsym_prepend_underscore to the CFLAGS then + * this function will be called, and will add the required underscore. + * + * Note that I haven't figured out yet which should be "standard", prepend + * the underscore always, or not at all. These global functions need to go away + * for opendarwin. + */ + int sym_len = strlen(symbol); + void *value = NULL; + char *malloc_sym = NULL; + malloc_sym = malloc(sym_len + 2); + if (malloc_sym) + { + sprintf(malloc_sym, "_%s", symbol); + value = dlsymIntern(handle, malloc_sym, 1); + free(malloc_sym); + } + else + { + error("Unable to allocate memory"); + } + return value; +} + +void *dlsym_auto_underscore(void *handle, const char *symbol) +{ + void *answer; + dolock(); + answer = dlsym_auto_underscore_intern(handle, symbol); + dounlock(); + return answer; + +} +static void *dlsym_auto_underscore_intern(void *handle, const char *symbol) +{ + struct dlstatus *dls = handle; + void *addr = 0; + addr = dlsymIntern(dls, symbol, 0); + if (!addr) + addr = dlsym_prepend_underscore_intern(handle, symbol); + return addr; +} + + +void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol) +{ + struct dlstatus *dls = handle; + void *addr = 0; + dolock(); + addr = dlsymIntern(dls, symbol, 1); + dounlock(); + return addr; +} +#endif + +int dlclose(void *handle) +{ + struct dlstatus *dls = handle; + dolock(); + resetdlerror(); + if (!isValidStatus(dls)) + { + goto dlcloseerror; + } + if (dls->module == MAGIC_DYLIB_MOD) + { + const char *name; + if (!dls->lib) + { + name = "global context"; + } + else + { + name = get_lib_name(dls->lib); + } + warning("trying to close a .dylib!"); + error("Not closing \"%s\" - dynamic libraries cannot be closed", name); + goto dlcloseerror; + } + if (!dls->module) + { + error("module already closed"); + goto dlcloseerror; + } + + if (dls->refs == 1) + { + unsigned long options = 0; + void (*fini) (void); + if ((fini = dlsymIntern(dls, "__fini", 0))) + { + debug("calling _fini()"); + fini(); + } + options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES; +#ifdef RTLD_NODELETE + if (isFlagSet(dls->mode, RTLD_NODELETE)) + options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED; +#endif + if (!NSUnLinkModule(dls->module, options)) + { + error("unable to unlink module"); + goto dlcloseerror; + } + dls->refs--; + dls->module = 0; + /* Note: the dlstatus struct dls is neither removed from the list + * nor is the memory it occupies freed. This shouldn't pose a + * problem in mostly all cases, though. + */ + } + dounlock(); + return 0; + dlcloseerror: + dounlock(); + return 1; +} + +char *dlerror(void) +{ + struct dlthread *tss; + const char * err_str = NULL; + dlcompat_init_check(); + tss = pthread_getspecific(dlerror_key); + if (tss != NULL && tss->errset != 0) { + tss->errset = 0; + err_str = tss->errstr; + } + return (err_str); +} + +/* Given an address, return the mach_header for the image containing it + * or zero if the given address is not contained in any loaded images. + */ +const struct mach_header *image_for_address(const void *address) +{ + unsigned long i; + unsigned long j; + unsigned long count = _dyld_image_count(); + struct mach_header *mh = 0; + struct load_command *lc = 0; + unsigned long addr = NULL; + for (i = 0; i < count; i++) + { + addr = (unsigned long)address - _dyld_get_image_vmaddr_slide(i); + mh = _dyld_get_image_header(i); + if (mh) + { + lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); + for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) + { + if (LC_SEGMENT == lc->cmd && + addr >= ((struct segment_command *)lc)->vmaddr && + addr < + ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize) + { + goto image_found; + } + } + } + mh = 0; + } + image_found: + return mh; +} + +int dladdr(const void * dl_restrict p, Dl_info * dl_restrict info) +{ +/* + FIXME: USe the routine image_for_address. +*/ + unsigned long i; + unsigned long j; + unsigned long count = _dyld_image_count(); + struct mach_header *mh = 0; + struct load_command *lc = 0; + unsigned long addr = NULL; + unsigned long table_off = (unsigned long)0; + int found = 0; + if (!info) + return 0; + dolock(); + resetdlerror(); + info->dli_fname = 0; + info->dli_fbase = 0; + info->dli_sname = 0; + info->dli_saddr = 0; +/* Some of this was swiped from code posted by Douglas Davidson + * to darwin-development AT lists DOT apple DOT com and slightly modified + */ + for (i = 0; i < count; i++) + { + addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(i); + mh = _dyld_get_image_header(i); + if (mh) + { + lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); + for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) + { + if (LC_SEGMENT == lc->cmd && + addr >= ((struct segment_command *)lc)->vmaddr && + addr < + ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize) + { + info->dli_fname = _dyld_get_image_name(i); + info->dli_fbase = (void *)mh; + found = 1; + break; + } + } + if (found) + break; + } + } + if (!found) + { + dounlock(); + return 0; + } + lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); + for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) + { + if (LC_SEGMENT == lc->cmd) + { + if (!strcmp(((struct segment_command *)lc)->segname, "__LINKEDIT")) + break; + } + } + table_off = + ((unsigned long)((struct segment_command *)lc)->vmaddr) - + ((unsigned long)((struct segment_command *)lc)->fileoff) + _dyld_get_image_vmaddr_slide(i); + debug("table off %x", table_off); + + lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); + for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) + { + if (LC_SYMTAB == lc->cmd) + { + + struct nlist *symtable = (struct nlist *)(((struct symtab_command *)lc)->symoff + table_off); + unsigned long numsyms = ((struct symtab_command *)lc)->nsyms; + struct nlist *nearest = NULL; + unsigned long diff = 0xffffffff; + unsigned long strtable = (unsigned long)(((struct symtab_command *)lc)->stroff + table_off); + debug("symtable %x", symtable); + for (i = 0; i < numsyms; i++) + { + /* Ignore the following kinds of Symbols */ + if ((!symtable->n_value) /* Undefined */ + || (symtable->n_type >= N_PEXT) /* Debug symbol */ + || (!(symtable->n_type & N_EXT)) /* Local Symbol */ + ) + { + symtable++; + continue; + } + if ((addr >= symtable->n_value) && (diff >= (symtable->n_value - addr))) + { + diff = (unsigned long)symtable->n_value - addr; + nearest = symtable; + } + symtable++; + } + if (nearest) + { + info->dli_saddr = nearest->n_value + ((void *)p - addr); + info->dli_sname = (char *)(strtable + nearest->n_un.n_strx); + } + } + } + dounlock(); + return 1; +} + + +/* + * Implement the dlfunc() interface, which behaves exactly the same as + * dlsym() except that it returns a function pointer instead of a data + * pointer. This can be used by applications to avoid compiler warnings + * about undefined behavior, and is intended as prior art for future + * POSIX standardization. This function requires that all pointer types + * have the same representation, which is true on all platforms FreeBSD + * runs on, but is not guaranteed by the C standard. + */ +#if 0 +dlfunc_t dlfunc(void * dl_restrict handle, const char * dl_restrict symbol) +{ + union + { + void *d; + dlfunc_t f; + } rv; + int sym_len = strlen(symbol); + char *malloc_sym = NULL; + dolock(); + malloc_sym = malloc(sym_len + 2); + if (malloc_sym) + { + sprintf(malloc_sym, "_%s", symbol); + rv.d = dlsymIntern(handle, malloc_sym, 1); + free(malloc_sym); + } + else + { + error("Unable to allocate memory"); + goto dlfuncerror; + } + dounlock(); + return rv.f; + dlfuncerror: + dounlock(); + return NULL; +} +#endif diff --git a/src/http.c b/src/http.c new file mode 100644 index 0000000..6b5580d --- /dev/null +++ b/src/http.c @@ -0,0 +1,137 @@ +/* Asterisk Manager Proxy + Copyright (c) 2005 David C. Troy + + This program is free software, distributed under the terms of + the GNU General Public License. + + HTTP Input Handler +*/ + +#include "astmanproxy.h" + +int ParseHTTPInput(char *buf, struct message *m) { + char *n, *v; + + /* just an empty block; go home + if ( !(*buf) ) + return 0; */ + + /* initialize message block */ + memset(m, 0, sizeof (struct message) ); + + n = buf; + while ( (v = strstr(n, "=")) ) { + v += 1; + debugmsg("n: %s, v: %s", n, v); + strncat(m->headers[m->hdrcount], n, v-n-1); + strcat(m->headers[m->hdrcount], ": "); + + if ( (n = strstr(v, "&")) ) { + n += 1; + } else { + n = (v + strlen(v) + 1); + } + strncat(m->headers[m->hdrcount], v, n-v-1); + debugmsg("got hdr: %s", m->headers[m->hdrcount]); + m->hdrcount++; + } + + return (m->hdrcount > 0); +} + +int BuildHTTPHeader(char *hdr) { + + + time_t t; + struct tm tm; + char date[80]; + + time(&t); + localtime_r(&t, &tm); + strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm); + + sprintf(hdr, + "HTTP/1.1 200 OK\r\n" + "Date: %s\r\n" + "Content-Type: text/xml\r\n" + "Connection: close\r\n" + "Server: %s/%s\r\n\r\n", + date, PROXY_BANNER, PROXY_VERSION); + + return 0; +} + +int _read(struct mansession *s, struct message *m) { + + /* Note: No single line may be longer than MAX_LEN/s->inbuf, as per get_input */ + /* No HTTP Input may be longer than BUFSIZE */ + + char line[MAX_LEN], method[10], formdata[MAX_LEN], header[MAX_LEN]; + int res, clength = 0; + + if (s->inputcomplete) + return 0; + + memset(method, 0, sizeof method); + memset(formdata, 0, sizeof formdata); + + /* for http, don't do get_input forever */ + for (;;) { + memset(line, 0, sizeof line); + res = get_input(s, line); + + if (res > 0) { + if (*line == '\0' ) { + if (*method == '\0') + break; + else { + if ( !strcasecmp(method, "POST") ) { + pthread_mutex_lock(&s->lock); + strncpy(formdata, s->inbuf, clength); + /* Move remaining data back to the front */ + memmove(s->inbuf, s->inbuf + clength+1, s->inlen - clength); + s->inlen -= clength; + pthread_mutex_unlock(&s->lock); + } + debugmsg("method: %s", method); + debugmsg("clength: %d", clength); + debugmsg("formdata: %s", formdata); + debugmsg("s->inbuf: %s", s->inbuf); + debugmsg("s->inlen: %d", s->inlen); + + BuildHTTPHeader(header); + pthread_mutex_lock(&s->lock); + s->inputcomplete = 1; + write(s->fd, header, strlen(header)); + pthread_mutex_unlock(&s->lock); + debugmsg("header: %s", header); + + /* now, let's transform and copy into a standard message block */ + res = ParseHTTPInput(formdata, m); + return res; + } + } else { + debugmsg("Got http: %s", line); + /* Do meaningful things here */ + if ( !strncmp(line,"POST",4) ) { + strncpy(method, line, 4); + } else if ( !strncmp(line,"GET",3) ) { + /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */ + strncpy(method, line, 3); + memcpy(formdata, line+6, strstr(line, " HTTP")-line-6); + } else if ( !strncasecmp(line, "Content-Length: ", 16) ) { + clength = atoi(line+16); + } + } + } else if (res < 0) + return res; + } + + return -1; +} + +int _autodisconnect() { + return 1; +} + +/* We do not define a _write or _onconnect method */ diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h new file mode 100644 index 0000000..f5718aa --- /dev/null +++ b/src/include/astmanproxy.h @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ + #include "dlfcn-compat.h" + #include "poll-compat.h" +#else + #include + #include +#endif + +#define BUFSIZE 150 +#define MAX_HEADERS 256 +#define MAX_LEN 150 + +#define PROXY_BANNER "Asterisk Call Manager Proxy" +#define PROXY_SHUTDOWN "ProxyMessage: Proxy Shutting Down" +#define ACTION_ID "ActionID" + +struct ast_server { + char nickname[80]; + char ast_host[40]; + char ast_port[10]; + char ast_user[80]; + char ast_pass[80]; + char ast_events[10]; + int status; /* TODO: have this mean something */ + struct ast_server *next; +}; + +struct proxy_user { + char username[80]; + char secret[80]; + char channel[80]; + char icontext[80]; + char ocontext[80]; + struct proxy_user *next; +}; + +struct proxyconfig { + struct ast_server *serverlist; + struct proxy_user *userlist; + char listen_addr[INET_ADDRSTRLEN]; + int listen_port; + char inputformat[80]; + char outputformat[80]; + int autofilter; + char key[80]; + char proc_user[30]; + char proc_group[30]; + char logfile[80]; + int retryinterval; + int maxretries; +}; + +struct iohandler { + int (*read) (); + int (*write) (); + int (*onconnect) (); + int *(*autodisconnect)(void); + char formatname[80]; + void *dlhandle; + struct iohandler *next; +}; + +struct mansession { + pthread_t t; + pthread_mutex_t lock; + struct sockaddr_in sin; + int fd; + char inbuf[MAX_LEN]; + int inlen; + struct iohandler *input; + struct iohandler *output; + int autofilter; + int inputcomplete; + int authenticated; + int connected; + struct ast_server *server; + struct proxy_user user; + char actionid[MAX_LEN]; + struct mansession *next; +}; + +struct message { + int hdrcount; + char headers[MAX_HEADERS][MAX_LEN]; + int in_command; + struct mansession *session; +}; + +struct proxyconfig pc; +extern int debug; + +/* Common Function Prototypes */ +void debugmsg (const char *, ...); +const char *ast_inet_ntoa(char *buf, int bufsiz, struct in_addr ia); +int AddHeader(struct message *m, const char *fmt, ...); +void debugmsg (const char *fmt, ...); +void logmsg (const char *fmt, ...); + +int StartServer(struct ast_server *srv); +int WriteAsterisk(struct message *m); +char *astman_get_header(struct message *m, char *var); +int proxyerror_do(struct mansession *s, char *err); +int get_input(struct mansession *s, char *output); +int SetIOHandlers(struct mansession *s, char *ifmt, char *ofmt); +void destroy_session(struct mansession *s); +int connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec); diff --git a/src/include/dlfcn-compat.h b/src/include/dlfcn-compat.h new file mode 100644 index 0000000..7c5e87f --- /dev/null +++ b/src/include/dlfcn-compat.h @@ -0,0 +1,83 @@ +/* +Copyright (c) 2002 Jorge Acereda & + Peter O'Gorman + +Portions may be copyright others, see the AUTHORS file included with this +distribution. + +Maintained by Peter O'Gorman + +Bug Reports and other queries should go to + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef _DLFCN_H_ +#define _DLFCN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined (__GNUC__) && __GNUC__ > 3 +#define dl_restrict __restrict +#else +#define dl_restrict +#endif + +#ifndef _POSIX_SOURCE +/* + * Structure filled in by dladdr(). + */ +typedef struct dl_info { + const char *dli_fname; /* Pathname of shared object */ + void *dli_fbase; /* Base address of shared object */ + const char *dli_sname; /* Name of nearest symbol */ + void *dli_saddr; /* Address of nearest symbol */ +} Dl_info; + +extern int dladdr(const void * dl_restrict, Dl_info * dl_restrict); +#endif /* ! _POSIX_SOURCE */ + +extern int dlclose(void * handle); +extern char * dlerror(void); +extern void * dlopen(const char *path, int mode); +extern void * dlsym(void * dl_restrict handle, const char * dl_restrict symbol); + +#define RTLD_LAZY 0x1 +#define RTLD_NOW 0x2 +#define RTLD_LOCAL 0x4 +#define RTLD_GLOBAL 0x8 + +#ifndef _POSIX_SOURCE +#define RTLD_NOLOAD 0x10 +#define RTLD_NODELETE 0x80 + +/* + * Special handle arguments for dlsym(). + */ +#define RTLD_NEXT ((void *) -1) /* Search subsequent objects. */ +#define RTLD_DEFAULT ((void *) -2) /* Use default search algorithm. */ +#endif /* ! _POSIX_SOURCE */ + +#ifdef __cplusplus +} +#endif + +#endif /* _DLFCN_H_ */ diff --git a/src/include/poll-compat.h b/src/include/poll-compat.h new file mode 100644 index 0000000..79eab15 --- /dev/null +++ b/src/include/poll-compat.h @@ -0,0 +1,101 @@ +/*---------------------------------------------------------------------------*\ + $Id: poll-compat.h,v 1.1 2003/10/26 18:50:49 markster Exp $ + + NAME + + poll - select(2)-based poll() emulation function for BSD systems. + + SYNOPSIS + #include "poll.h" + + struct pollfd + { + int fd; + short events; + short revents; + } + + int poll (struct pollfd *pArray, unsigned long n_fds, int timeout) + + DESCRIPTION + + This file, and the accompanying "poll.c", implement the System V + poll(2) system call for BSD systems (which typically do not provide + poll()). Poll() provides a method for multiplexing input and output + on multiple open file descriptors; in traditional BSD systems, that + capability is provided by select(). While the semantics of select() + differ from those of poll(), poll() can be readily emulated in terms + of select() -- which is how this function is implemented. + + REFERENCES + Stevens, W. Richard. Unix Network Programming. Prentice-Hall, 1990. + + NOTES + 1. This software requires an ANSI C compiler. + + LICENSE + + This software is released under the following license: + + Copyright (c) 1995-2002 Brian M. Clapper + All rights reserved. + + Redistribution and use in source and binary forms are + permitted provided that: (1) source distributions retain + this entire copyright notice and comment; (2) modifications + made to the software are prominently mentioned, and a copy + of the original software (or a pointer to its location) are + included; and (3) distributions including binaries display + the following acknowledgement: "This product includes + software developed by Brian M. Clapper " + in the documentation or other materials provided with the + distribution. The name of the author may not be used to + endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE. + + Effectively, this means you can do what you want with the software + except remove this notice or take advantage of the author's name. + If you modify the software and redistribute your modified version, + you must indicate that your version is a modification of the + original, and you must provide either a pointer to or a copy of the + original. +\*---------------------------------------------------------------------------*/ + +#ifndef _POLL_EMUL_H_ +#define _POLL_EMUL_H_ + +#define POLLIN 0x01 +#define POLLPRI 0x02 +#define POLLOUT 0x04 +#define POLLERR 0x08 +#define POLLHUP 0x10 +#define POLLNVAL 0x20 + +struct pollfd +{ + int fd; + short events; + short revents; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if (__STDC__ > 0) || defined(__cplusplus) +extern int poll (struct pollfd *pArray, unsigned long n_fds, int timeout); +#else +extern int poll(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _POLL_EMUL_H_ */ diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..d3f8d15 --- /dev/null +++ b/src/log.c @@ -0,0 +1,57 @@ +#include "astmanproxy.h" + +#define DATEFORMAT "%b %e %T" + +extern FILE *proxylog; +extern int debug; +extern pthread_mutex_t loglock; +extern pthread_mutex_t debuglock; + +void debugmsg (const char *fmt, ...) +{ + va_list ap; + + time_t t; + struct tm tm; + char date[80]; + + if (!debug) + return; + + time(&t); + localtime_r(&t, &tm); + strftime(date, sizeof(date), DATEFORMAT, &tm); + + pthread_mutex_lock(&debuglock); + va_start(ap, fmt); + printf("%s: ", date); + vprintf(fmt, ap); + printf("\n"); + va_end(ap); + pthread_mutex_unlock(&debuglock); +} + + +void logmsg (const char *fmt, ...) +{ + va_list ap; + + time_t t; + struct tm tm; + char date[80]; + + time(&t); + localtime_r(&t, &tm); + strftime(date, sizeof(date), DATEFORMAT, &tm); + + if (proxylog) { + pthread_mutex_lock(&loglock); + va_start(ap, fmt); + fprintf(proxylog, "%s: ", date); + vfprintf(proxylog, fmt, ap); + fprintf(proxylog, "\n"); + va_end(ap); + fflush(proxylog); + pthread_mutex_unlock(&loglock); + } +} diff --git a/src/poll.c b/src/poll.c new file mode 100644 index 0000000..a36539a --- /dev/null +++ b/src/poll.c @@ -0,0 +1,306 @@ +/*---------------------------------------------------------------------------*\ + $Id: poll.c,v 1.1 2003/10/26 19:17:28 tholo Exp $ + + NAME + + poll - select(2)-based poll() emulation function for BSD systems. + + SYNOPSIS + #include "poll.h" + + struct pollfd + { + int fd; + short events; + short revents; + } + + int poll (struct pollfd *pArray, unsigned long n_fds, int timeout) + + DESCRIPTION + + This file, and the accompanying "poll.h", implement the System V + poll(2) system call for BSD systems (which typically do not provide + poll()). Poll() provides a method for multiplexing input and output + on multiple open file descriptors; in traditional BSD systems, that + capability is provided by select(). While the semantics of select() + differ from those of poll(), poll() can be readily emulated in terms + of select() -- which is how this function is implemented. + + REFERENCES + Stevens, W. Richard. Unix Network Programming. Prentice-Hall, 1990. + + NOTES + 1. This software requires an ANSI C compiler. + + LICENSE + + This software is released under the following license: + + Copyright (c) 1995-2002 Brian M. Clapper + All rights reserved. + + Redistribution and use in source and binary forms are + permitted provided that: (1) source distributions retain + this entire copyright notice and comment; (2) modifications + made to the software are prominently mentioned, and a copy + of the original software (or a pointer to its location) are + included; and (3) distributions including binaries display + the following acknowledgement: "This product includes + software developed by Brian M. Clapper " + in the documentation or other materials provided with the + distribution. The name of the author may not be used to + endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE. + + Effectively, this means you can do what you want with the software + except remove this notice or take advantage of the author's name. + If you modify the software and redistribute your modified version, + you must indicate that your version is a modification of the + original, and you must provide either a pointer to or a copy of the + original. +\*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*\ + Includes +\*---------------------------------------------------------------------------*/ + +#include /* standard Unix definitions */ +#include /* system types */ +#include /* time definitions */ +#include /* assertion macros */ +#include /* string functions */ + +#include "poll-compat.h" /* this package */ + +/*---------------------------------------------------------------------------*\ + Macros +\*---------------------------------------------------------------------------*/ + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + + +/*---------------------------------------------------------------------------*\ + Private Functions +\*---------------------------------------------------------------------------*/ + +static int map_poll_spec +#if __STDC__ > 0 + (struct pollfd *pArray, + unsigned long n_fds, + fd_set *pReadSet, + fd_set *pWriteSet, + fd_set *pExceptSet) +#else + (pArray, n_fds, pReadSet, pWriteSet, pExceptSet) + struct pollfd *pArray; + unsigned long n_fds; + fd_set *pReadSet; + fd_set *pWriteSet; + fd_set *pExceptSet; +#endif +{ + register unsigned long i; /* loop control */ + register struct pollfd *pCur; /* current array element */ + register int max_fd = -1; /* return value */ + + /* + Map the poll() structures into the file descriptor sets required + by select(). + */ + for (i = 0, pCur = pArray; i < n_fds; i++, pCur++) + { + /* Skip any bad FDs in the array. */ + + if (pCur->fd < 0) + continue; + + if (pCur->events & POLLIN) + { + /* "Input Ready" notification desired. */ + FD_SET (pCur->fd, pReadSet); + } + + if (pCur->events & POLLOUT) + { + /* "Output Possible" notification desired. */ + FD_SET (pCur->fd, pWriteSet); + } + + if (pCur->events & POLLPRI) + { + /* + "Exception Occurred" notification desired. (Exceptions + include out of band data. + */ + FD_SET (pCur->fd, pExceptSet); + } + + max_fd = MAX (max_fd, pCur->fd); + } + + return max_fd; +} + +static struct timeval *map_timeout +#if __STDC__ > 0 + (int poll_timeout, struct timeval *pSelTimeout) +#else + (poll_timeout, pSelTimeout) + int poll_timeout; + struct timeval *pSelTimeout; +#endif +{ + struct timeval *pResult; + + /* + Map the poll() timeout value into a select() timeout. The possible + values of the poll() timeout value, and their meanings, are: + + VALUE MEANING + + -1 wait indefinitely (until signal occurs) + 0 return immediately, don't block + >0 wait specified number of milliseconds + + select() uses a "struct timeval", which specifies the timeout in + seconds and microseconds, so the milliseconds value has to be mapped + accordingly. + */ + + assert (pSelTimeout != (struct timeval *) NULL); + + switch (poll_timeout) + { + case -1: + /* + A NULL timeout structure tells select() to wait indefinitely. + */ + pResult = (struct timeval *) NULL; + break; + + case 0: + /* + "Return immediately" (test) is specified by all zeros in + a timeval structure. + */ + pSelTimeout->tv_sec = 0; + pSelTimeout->tv_usec = 0; + pResult = pSelTimeout; + break; + + default: + /* Wait the specified number of milliseconds. */ + pSelTimeout->tv_sec = poll_timeout / 1000; /* get seconds */ + poll_timeout %= 1000; /* remove seconds */ + pSelTimeout->tv_usec = poll_timeout * 1000; /* get microseconds */ + pResult = pSelTimeout; + break; + } + + + return pResult; +} + +static void map_select_results +#if __STDC__ > 0 + (struct pollfd *pArray, + unsigned long n_fds, + fd_set *pReadSet, + fd_set *pWriteSet, + fd_set *pExceptSet) +#else + (pArray, n_fds, pReadSet, pWriteSet, pExceptSet) + struct pollfd *pArray; + unsigned long n_fds; + fd_set *pReadSet; + fd_set *pWriteSet; + fd_set *pExceptSet; +#endif +{ + register unsigned long i; /* loop control */ + register struct pollfd *pCur; /* current array element */ + + for (i = 0, pCur = pArray; i < n_fds; i++, pCur++) + { + /* Skip any bad FDs in the array. */ + + if (pCur->fd < 0) + continue; + + /* Exception events take priority over input events. */ + + pCur->revents = 0; + if (FD_ISSET (pCur->fd, pExceptSet)) + pCur->revents |= POLLPRI; + + else if (FD_ISSET (pCur->fd, pReadSet)) + pCur->revents |= POLLIN; + + if (FD_ISSET (pCur->fd, pWriteSet)) + pCur->revents |= POLLOUT; + } + + return; +} + +/*---------------------------------------------------------------------------*\ + Public Functions +\*---------------------------------------------------------------------------*/ + +int poll + +#if __STDC__ > 0 + (struct pollfd *pArray, unsigned long n_fds, int timeout) +#else + (pArray, n_fds, timeout) + struct pollfd *pArray; + unsigned long n_fds; + int timeout; +#endif + +{ + fd_set read_descs; /* input file descs */ + fd_set write_descs; /* output file descs */ + fd_set except_descs; /* exception descs */ + struct timeval stime; /* select() timeout value */ + int ready_descriptors; /* function result */ + int max_fd; /* maximum fd value */ + struct timeval *pTimeout; /* actually passed */ + + FD_ZERO (&read_descs); + FD_ZERO (&write_descs); + FD_ZERO (&except_descs); + + assert (pArray != (struct pollfd *) NULL); + + /* Map the poll() file descriptor list in the select() data structures. */ + + max_fd = map_poll_spec (pArray, n_fds, + &read_descs, &write_descs, &except_descs); + + /* Map the poll() timeout value in the select() timeout structure. */ + + pTimeout = map_timeout (timeout, &stime); + + /* Make the select() call. */ + + ready_descriptors = select (max_fd + 1, &read_descs, &write_descs, + &except_descs, pTimeout); + + if (ready_descriptors >= 0) + { + map_select_results (pArray, n_fds, + &read_descs, &write_descs, &except_descs); + } + + return ready_descriptors; +} diff --git a/src/proxyfunc.c b/src/proxyfunc.c new file mode 100644 index 0000000..51ed5c6 --- /dev/null +++ b/src/proxyfunc.c @@ -0,0 +1,336 @@ +#include "astmanproxy.h" + +extern struct mansession *sessions; +extern struct iohandler *iohandlers; +extern pthread_mutex_t serverlock; +extern pthread_mutex_t userslock; + +void *ProxyListIOHandlers(struct mansession *s) { + struct message m; + struct iohandler *i; + + memset(&m, 0, sizeof(struct message)); + AddHeader(&m, "ProxyResponse: Success"); + + i = iohandlers; + while (i && (m.hdrcount < MAX_HEADERS - 1) ) { + if (i->read) + AddHeader(&m, "InputHandler: %s", i->formatname); + if (i->write) + AddHeader(&m, "OutputHandler: %s", i->formatname); + i = i->next; + } + + s->output->write(s, &m); + return 0; +} + +void *ProxyListSessions(struct mansession *s) { + struct message m; + struct mansession *c; + char iabuf[INET_ADDRSTRLEN]; + + memset(&m, 0, sizeof(struct message)); + AddHeader(&m, "ProxyResponse: Success"); + + c = sessions; + while (c && (m.hdrcount < MAX_HEADERS - 1) ) { + if (!c->server) { + AddHeader(&m, "ProxyClientSession: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr), c->actionid); + AddHeader(&m, "ProxyClientInputHandler: %s", c->input->formatname); + AddHeader(&m, "ProxyClientOutputHandler: %s", c->output->formatname); + } else + AddHeader(&m, "ProxyServerSession: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); + c = c->next; + } + s->output->write(s, &m); + return 0; +} + +void *ProxySetOutputFormat(struct mansession *s, struct message *m) { + struct message mo; + char *value; + + value = astman_get_header(m, "OutputFormat"); + SetIOHandlers(s, s->input->formatname, value); + /* TODO: this is retarded */ + + memset(&mo, 0, sizeof(struct message)); + AddHeader(&mo, "ProxyResponse: Success"); + AddHeader(&mo, "OutputFormat: %s", s->output->formatname ); + + s->output->write(s, &mo); + + return 0; +} + +void *ProxySetAutoFilter(struct mansession *s, struct message *m) { + struct message mo; + char *value; + int i; + + value = astman_get_header(m, "AutoFilter"); + if ( !strcasecmp(value, "on") ) + i = 1; + else + i = 0; + pthread_mutex_lock(&s->lock); + s->autofilter = i; + pthread_mutex_unlock(&s->lock); + + memset(&mo, 0, sizeof(struct message)); + AddHeader(&mo, "ProxyResponse: Success"); + AddHeader(&mo, "AutoFilter: %d", s->autofilter); + + s->output->write(s, &mo); + + return 0; +} + +void *ProxyLogin(struct mansession *s, char *user, char *secret) { + struct message m; + struct proxy_user *pu; + + memset(&m, 0, sizeof(struct message)); + if( debug ) + debugmsg("Login attempt as: %s/%s", user, secret); + + pthread_mutex_lock(&userslock); + pu = pc.userlist; + while( pu ) { + if ( !strcmp(user, pu->username) && !strcmp(secret, pu->secret) ) { + AddHeader(&m, "Response: Success"); + AddHeader(&m, "Message: Authentication accepted"); + s->output->write(s, &m); + s->authenticated = 1; + strcpy(s->user.channel, pu->channel); + strcpy(s->user.icontext, pu->icontext); + strcpy(s->user.ocontext, pu->ocontext); + if( debug ) + debugmsg("Login as: %s", user); + break; + } + pu = pu->next; + } + pthread_mutex_unlock(&userslock); + + if( !pu ) { + AddHeader(&m, "Response: Error"); + AddHeader(&m, "Message: Authentication failed"); + s->output->write(s, &m); + s->authenticated = 0; + if( debug ) + debugmsg("Login failed as: %s/%s", user, secret); + } + + return 0; +} + +void *ProxyLogoff(struct mansession *s) { + struct message m; + + memset(&m, 0, sizeof(struct message)); + AddHeader(&m, "Goodbye: Y'all come back now, y'hear?"); + + s->output->write(s, &m); + + destroy_session(s); + if (debug) + debugmsg("Client logged off - exiting thread"); + pthread_exit(NULL); + return 0; +} + +int ProxyAddServer(struct mansession *s, struct message *m) { + struct message mo; + struct ast_server *srv; + int res = 0; + + /* malloc ourselves a server credentials structure */ + srv = malloc(sizeof(struct ast_server)); + if ( !srv ) { + fprintf(stderr, "Failed to allocate server credentials: %s\n", strerror(errno)); + exit(1); + } + memset(srv, 0, sizeof (struct ast_server) ); + + + /* TODO: Disallow adding of duplicate servers? Or not, I suppose that could be useful (events on/off) */ + memset(srv, 0, sizeof(struct ast_server) ); + memset(&mo, 0, sizeof(struct message)); + strcpy(srv->ast_host, astman_get_header(m, "Server")); + strcpy(srv->ast_user, astman_get_header(m, "Username")); + strcpy(srv->ast_pass, astman_get_header(m, "Secret")); + strcpy(srv->ast_port, astman_get_header(m, "Port")); + strcpy(srv->ast_events, astman_get_header(m, "Events")); + + if (*srv->ast_host && *srv->ast_user && *srv->ast_pass && *srv->ast_port && *srv->ast_events) { + pthread_mutex_lock(&serverlock); + srv->next = pc.serverlist; + pc.serverlist = srv; + pthread_mutex_unlock(&serverlock); + res = StartServer(srv); + } else + res = 1; + + if (res) { + AddHeader(&mo, "ProxyResponse: Failure"); + AddHeader(&mo, "Message: Could not add %s", srv->ast_host); + } else { + AddHeader(&mo, "ProxyResponse: Success"); + AddHeader(&mo, "Message: Added %s", srv->ast_host); + } + + s->output->write(s, &mo); + return 0; +} + +int ProxyDropServer(struct mansession *s, struct message *m) { + struct message mo; + struct mansession *srv; + char *value; + int res; + + memset(&mo, 0, sizeof(struct message)); + value = astman_get_header(m, "Server"); + srv = sessions; + while (*value && srv) { + if (srv->server && !strcmp(srv->server->ast_host, value)) + break; + srv = srv->next; + } + + if (srv) { + destroy_session(srv); + debugmsg("Dropping Server %s", value); + AddHeader(&mo, "ProxyResponse: Success"); + AddHeader(&mo, "Message: Dropped %s", value); + res = 0; + } else { + debugmsg("Failed to Drop Server %s -- not found", value); + AddHeader(&mo, "ProxyResponse: Failure"); + AddHeader(&mo, "Message: Cannot Drop Server %s, Does Not Exist", value); + res = 1; + } + + s->output->write(s, &mo); + return res; +} + +void *ProxyListServers(struct mansession *s) { + struct message m; + struct mansession *c; + char iabuf[INET_ADDRSTRLEN]; + + memset(&m, 0, sizeof(struct message)); + AddHeader(&m, "ProxyResponse: Success"); + + c = sessions; + while (c) { + if (c->server) { + AddHeader(&m, "ProxyListServer I: %s H: %s U: %s P: %s E: %s ", + ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr), + c->server->ast_host, c->server->ast_user, + c->server->ast_port, c->server->ast_events); + } + + c = c->next; + } + s->output->write(s, &m); + return 0; +} + + +void *proxyaction_do(char *proxyaction, struct message *m, struct mansession *s) +{ + if (!strcasecmp(proxyaction,"SetOutputFormat")) + ProxySetOutputFormat(s, m); + else if (!strcasecmp(proxyaction,"SetAutoFilter")) + ProxySetAutoFilter(s, m); + else if (!strcasecmp(proxyaction,"ListSessions")) + ProxyListSessions(s); + else if (!strcasecmp(proxyaction,"AddServer")) + ProxyAddServer(s, m); + else if (!strcasecmp(proxyaction,"DropServer")) + ProxyDropServer(s, m); + else if (!strcasecmp(proxyaction,"ListServers")) + ProxyListServers(s); + else if (!strcasecmp(proxyaction,"ListIOHandlers")) + ProxyListIOHandlers(s); + else if (!strcasecmp(proxyaction,"Logoff")) + ProxyLogoff(s); + else + proxyerror_do(s, "Invalid Proxy Action"); + + return 0; +} + +int proxyerror_do(struct mansession *s, char *err) +{ + struct message mo; + + memset(&mo, 0, sizeof(struct message)); + AddHeader(&mo, "ProxyResponse: Error"); + AddHeader(&mo, "Message: %s", err); + + s->output->write(s, &mo); + + return 0; +} + +int ValidateAction(struct message *m, struct mansession *s, int inbound) { + char *channel, *channel1, *channel2; + char *context; + char *uchannel; + char *ucontext; + + if( !s->authenticated ) + return 0; + + if( inbound ) + ucontext = s->user.icontext; + else + ucontext = s->user.ocontext; + uchannel = s->user.channel; + + channel = astman_get_header(m, "Channel"); + if( channel[0] != '\0' && uchannel[0] != '\0' ) + if( strncasecmp( channel, uchannel, strlen(uchannel) ) ) { + if( debug ) + debugmsg("Message filtered (chan): %s != %s", channel, uchannel); + return 0; + } + + channel1 = astman_get_header(m, "Channel1"); + channel2 = astman_get_header(m, "Channel2"); + if( (channel1[0] != '\0' || channel2[0] != '\0') && uchannel[0] != '\0' ) + if( !(strncasecmp( channel1, uchannel, strlen(uchannel) ) == 0 || + strncasecmp( channel2, uchannel, strlen(uchannel) ) == 0) ) { + if( debug ) + debugmsg("Message filtered (chan): %s/%s != %s", channel1, channel2, uchannel); + return 0; + } + + context = astman_get_header(m, "Context"); + if( context[0] != '\0' && ucontext[0] != '\0' ) + if( strcasecmp( context, ucontext ) ) { + if( debug ) + debugmsg("Message filtered (ctxt): %s != %s", context, ucontext); + return 0; + } + + return 1; +} + +void *SendError(struct mansession *s) { + struct message m; + + memset(&m, 0, sizeof(struct message)); + AddHeader(&m, "Response: Error"); + AddHeader(&m, "Message: Action Filtered"); + + s->output->write(s, &m); + + return 0; +} + diff --git a/src/standard.c b/src/standard.c new file mode 100644 index 0000000..5933e45 --- /dev/null +++ b/src/standard.c @@ -0,0 +1,79 @@ +/* Asterisk Manager Proxy + Copyright (c) 2005 David C. Troy + + This program is free software, distributed under the terms of + the GNU General Public License. + + Standard I/O Handler +*/ + +#include "astmanproxy.h" + +extern struct mansession *sessions; + +/* Return a fully formed message block to session_do for processing */ +int _read(struct mansession *s, struct message *m) { + int res; + + if (debug) debugmsg("in standard_read module..."); + for (;;) { + res = get_input(s, m->headers[m->hdrcount]); + + /*fprintf(stderr, "-------> %s\n", m->headers[m->hdrcount]);*/ + if (strstr(m->headers[m->hdrcount], "--END COMMAND--")) { + if (debug) debugmsg("Found END COMMAND"); + m->in_command = 0; + } + if (strstr(m->headers[m->hdrcount], "Response: Follows")) { + if (debug) debugmsg("Found Response Follows"); + m->in_command = 1; + } + if (res > 0) { + if (!m->in_command && *(m->headers[m->hdrcount]) == '\0' ) { + break; + } else if (m->hdrcount < MAX_HEADERS - 1) { + m->hdrcount++; + } else { + m->in_command = 0; // reset when block full + } + } else if (res < 0) + break; + } + + return res; +} + +int _write(struct mansession *s, struct message *m) { + int i; + + if (debug) debugmsg("in standard_write module..."); + pthread_mutex_lock(&s->lock); + for (i=0; ihdrcount; i++) { + if( strncasecmp( m->headers[i], "CallerID: ", 19 ) == 0 ) { + write(s->fd, "CallerID: ", 10); + } else { + write(s->fd, m->headers[i], strlen(m->headers[i]) ); + } + write(s->fd, "\r\n", 2); + } + write(s->fd, "\r\n", 2); + pthread_mutex_unlock(&s->lock); + + return 0; +} + +int _onconnect(struct mansession *s, struct message *m) { + + char banner[100]; + + sprintf(banner, "%s/%s\r\n", PROXY_BANNER, PROXY_VERSION); + pthread_mutex_lock(&s->lock); + write(s->fd, banner, strlen(banner)); + pthread_mutex_unlock(&s->lock); + + return 0; +} + +int _autodisconnect() { + return 0; +} diff --git a/src/xml.c b/src/xml.c new file mode 100644 index 0000000..0d6737b --- /dev/null +++ b/src/xml.c @@ -0,0 +1,163 @@ +/* Asterisk Manager Proxy + Copyright (c) 2005 David C. Troy + + This program is free software, distributed under the terms of + the GNU General Public License. + + XML I/O Handler +*/ + +#include "astmanproxy.h" + +#define XML_UNPARSED "UnparsedText" +#define XML_BEGIN_INPUT "" +#define XML_END_INPUT "" + +#define XML_SERVERTAG "AsteriskManagerOutput" +#define XML_PROXYTAG "AsteriskManagerProxyOutput" + +void xml_quote_string(char *s, char *o); +int ParseXMLInput(char *xb, struct message *m); + +int _read(struct mansession *s, struct message *m) { + + /* Note: No single line may be longer than MAX_LEN/s->inbuf, as per get_input */ + /* No XML Input may be longer than BUFSIZE */ + + char line[MAX_LEN], xmlbuf[BUFSIZE]; + int res; + + /* first let's read the whole xml block into our buffer */ + memset(xmlbuf, 0, sizeof xmlbuf); + for (;;) { + memset(line, 0, sizeof line); + res = get_input(s, line); + + if (res > 0) { + if (*line == '\0' ) { + break; + } else if (strlen(xmlbuf) < (BUFSIZE - strlen(line)) ) + strcat(xmlbuf, line); + } else if (res < 0) + return res; + } + + /* now, let's transform and copy into a standard message block */ + debugmsg("Got xml: %s", xmlbuf); + res = ParseXMLInput(xmlbuf, m); + + if (res < 0) + proxyerror_do(s, "Invalid XML Input"); + + /* Return res>0 to process block, return res<0 to kill client, res=0, continue */ + return res; +} + +void *setdoctag(char *tag, struct mansession *s) { + + /* if message came from a server, say so; otherwise it must be from proxy */ + /* right now there is no such thing as client<->client comms */ + if (s && s->server) + strcpy(tag, XML_SERVERTAG); + else + strcpy(tag, XML_PROXYTAG); + + return 0; +} + +int _write(struct mansession *s, struct message *m) { + int i; + char buf[BUFSIZE], outstring[MAX_LEN*3], xmlescaped[MAX_LEN*3], xmldoctag[MAX_LEN]; + char *dpos, *lpos; + + setdoctag(xmldoctag, m->session); + sprintf(buf, "<%s>\r\n", xmldoctag); + + pthread_mutex_lock(&s->lock); + write(s->fd, buf, strlen(buf)); + + for (i=0; ihdrcount; i++) { + memset(xmlescaped, 0, sizeof xmlescaped); + xml_quote_string(m->headers[i], xmlescaped); + lpos = xmlescaped; + dpos = strstr(lpos, ": "); + if (dpos) { + strcpy(outstring, " <"); + strncat(outstring, lpos, dpos-lpos); + strcat(outstring, " Value=\""); + strncat(outstring, dpos+2, strlen(dpos)-2); + strcat(outstring, "\"/>\r\n"); + } else + sprintf(outstring, " <%s Value=\"%s\"/>\r\n", XML_UNPARSED, lpos); + write(s->fd, outstring, strlen(outstring) ); + } + sprintf(buf, "\r\n\r\n", xmldoctag); + write(s->fd, buf, strlen(buf)); + pthread_mutex_unlock(&s->lock); + + return 0; +} + +int _autodisconnect() { + return 0; +} + + +/* Takes a single manager header line and converts xml entities */ +void xml_quote_string(char *s, char *o) { + + char *c; + c = s; + + do { + if (*c == '<') + strcat(o, "<"); + else if (*c == '>') + strcat(o, ">"); + else if (*c == '&') + strcat(o, "&"); + else if (*c == '"') + strcat(o, """); + else if (*c == '\n') + strcat(o, " "); + else + strncat(o, c, 1); + } while (*(c++)); + + return; +} + +int ParseXMLInput(char *xb, struct message *m) { + char *b, *e, *bt, *et, tag[MAX_LEN], *i; + int res = 0; + + /* just an empty block; go home */ + if ( !(*xb) ) + return 0; + + /* initialize message block */ + memset(m, 0, sizeof(struct message) ); + + b = strstr(xb, XML_BEGIN_INPUT); + e = strstr(xb, XML_END_INPUT); + if (b && e) { + bt = strstr((char *)(b + strlen(XML_BEGIN_INPUT) + 1), "<"); + while (bt < e) { + et = strstr(bt+1, "<"); + memset(tag, 0, sizeof tag); + strncpy(tag, bt, (et-bt) ); + bt = et; + + strncpy( m->headers[m->hdrcount], tag+1, strstr(tag+1," ")-(tag+1) ); + strcat(m->headers[m->hdrcount], ": "); + i = strstr(tag+1, "\"") + 1; + strncat( m->headers[m->hdrcount], i, strstr(i, "\"") - i ); + debugmsg("parsed: %s", m->headers[m->hdrcount]); + m->hdrcount++; + } + res = 1; + } else + res = -1; + + return res; +} diff --git a/standard.c b/standard.c deleted file mode 100644 index de62cf7..0000000 --- a/standard.c +++ /dev/null @@ -1,75 +0,0 @@ -/* Asterisk Manager Proxy - Copyright (c) 2005 David C. Troy - - This program is free software, distributed under the terms of - the GNU General Public License. - - Standard I/O Handler -*/ - -#include "astmanproxy.h" - -extern struct mansession *sessions; - -/* Return a fully formed message block to session_do for processing */ -int _read(struct mansession *s, struct message *m) { - int res; - - if (debug) debugmsg("in standard_read module..."); - for (;;) { - res = get_input(s, m->headers[m->hdrcount]); - - /*fprintf(stderr, "-------> %s\n", m->headers[m->hdrcount]);*/ - if (strstr(m->headers[m->hdrcount], "--END COMMAND--")) { - if (debug) debugmsg("Found END COMMAND"); - m->in_command = 0; - } - if (strstr(m->headers[m->hdrcount], "Response: Follows")) { - if (debug) debugmsg("Found Response Follows"); - m->in_command = 1; - } - if (res > 0) { - if (!m->in_command && *(m->headers[m->hdrcount]) == '\0' ) { - break; - } else if (m->hdrcount < MAX_HEADERS - 1) { - m->hdrcount++; - } else { - m->in_command = 0; // reset when block full - } - } else if (res < 0) - break; - } - - return res; -} - -int _write(struct mansession *s, struct message *m) { - int i; - - if (debug) debugmsg("in standard_write module..."); - pthread_mutex_lock(&s->lock); - for (i=0; ihdrcount; i++) { - write(s->fd, m->headers[i], strlen(m->headers[i]) ); - write(s->fd, "\r\n", 2); - } - write(s->fd, "\r\n", 2); - pthread_mutex_unlock(&s->lock); - - return 0; -} - -int _onconnect(struct mansession *s, struct message *m) { - - char banner[100]; - - sprintf(banner, "%s/%s\r\n", PROXY_BANNER, PROXY_VERSION); - pthread_mutex_lock(&s->lock); - write(s->fd, banner, strlen(banner)); - pthread_mutex_unlock(&s->lock); - - return 0; -} - -int _autodisconnect() { - return 0; -} diff --git a/xml.c b/xml.c deleted file mode 100644 index 0d6737b..0000000 --- a/xml.c +++ /dev/null @@ -1,163 +0,0 @@ -/* Asterisk Manager Proxy - Copyright (c) 2005 David C. Troy - - This program is free software, distributed under the terms of - the GNU General Public License. - - XML I/O Handler -*/ - -#include "astmanproxy.h" - -#define XML_UNPARSED "UnparsedText" -#define XML_BEGIN_INPUT "" -#define XML_END_INPUT "" - -#define XML_SERVERTAG "AsteriskManagerOutput" -#define XML_PROXYTAG "AsteriskManagerProxyOutput" - -void xml_quote_string(char *s, char *o); -int ParseXMLInput(char *xb, struct message *m); - -int _read(struct mansession *s, struct message *m) { - - /* Note: No single line may be longer than MAX_LEN/s->inbuf, as per get_input */ - /* No XML Input may be longer than BUFSIZE */ - - char line[MAX_LEN], xmlbuf[BUFSIZE]; - int res; - - /* first let's read the whole xml block into our buffer */ - memset(xmlbuf, 0, sizeof xmlbuf); - for (;;) { - memset(line, 0, sizeof line); - res = get_input(s, line); - - if (res > 0) { - if (*line == '\0' ) { - break; - } else if (strlen(xmlbuf) < (BUFSIZE - strlen(line)) ) - strcat(xmlbuf, line); - } else if (res < 0) - return res; - } - - /* now, let's transform and copy into a standard message block */ - debugmsg("Got xml: %s", xmlbuf); - res = ParseXMLInput(xmlbuf, m); - - if (res < 0) - proxyerror_do(s, "Invalid XML Input"); - - /* Return res>0 to process block, return res<0 to kill client, res=0, continue */ - return res; -} - -void *setdoctag(char *tag, struct mansession *s) { - - /* if message came from a server, say so; otherwise it must be from proxy */ - /* right now there is no such thing as client<->client comms */ - if (s && s->server) - strcpy(tag, XML_SERVERTAG); - else - strcpy(tag, XML_PROXYTAG); - - return 0; -} - -int _write(struct mansession *s, struct message *m) { - int i; - char buf[BUFSIZE], outstring[MAX_LEN*3], xmlescaped[MAX_LEN*3], xmldoctag[MAX_LEN]; - char *dpos, *lpos; - - setdoctag(xmldoctag, m->session); - sprintf(buf, "<%s>\r\n", xmldoctag); - - pthread_mutex_lock(&s->lock); - write(s->fd, buf, strlen(buf)); - - for (i=0; ihdrcount; i++) { - memset(xmlescaped, 0, sizeof xmlescaped); - xml_quote_string(m->headers[i], xmlescaped); - lpos = xmlescaped; - dpos = strstr(lpos, ": "); - if (dpos) { - strcpy(outstring, " <"); - strncat(outstring, lpos, dpos-lpos); - strcat(outstring, " Value=\""); - strncat(outstring, dpos+2, strlen(dpos)-2); - strcat(outstring, "\"/>\r\n"); - } else - sprintf(outstring, " <%s Value=\"%s\"/>\r\n", XML_UNPARSED, lpos); - write(s->fd, outstring, strlen(outstring) ); - } - sprintf(buf, "\r\n\r\n", xmldoctag); - write(s->fd, buf, strlen(buf)); - pthread_mutex_unlock(&s->lock); - - return 0; -} - -int _autodisconnect() { - return 0; -} - - -/* Takes a single manager header line and converts xml entities */ -void xml_quote_string(char *s, char *o) { - - char *c; - c = s; - - do { - if (*c == '<') - strcat(o, "<"); - else if (*c == '>') - strcat(o, ">"); - else if (*c == '&') - strcat(o, "&"); - else if (*c == '"') - strcat(o, """); - else if (*c == '\n') - strcat(o, " "); - else - strncat(o, c, 1); - } while (*(c++)); - - return; -} - -int ParseXMLInput(char *xb, struct message *m) { - char *b, *e, *bt, *et, tag[MAX_LEN], *i; - int res = 0; - - /* just an empty block; go home */ - if ( !(*xb) ) - return 0; - - /* initialize message block */ - memset(m, 0, sizeof(struct message) ); - - b = strstr(xb, XML_BEGIN_INPUT); - e = strstr(xb, XML_END_INPUT); - if (b && e) { - bt = strstr((char *)(b + strlen(XML_BEGIN_INPUT) + 1), "<"); - while (bt < e) { - et = strstr(bt+1, "<"); - memset(tag, 0, sizeof tag); - strncpy(tag, bt, (et-bt) ); - bt = et; - - strncpy( m->headers[m->hdrcount], tag+1, strstr(tag+1," ")-(tag+1) ); - strcat(m->headers[m->hdrcount], ": "); - i = strstr(tag+1, "\"") + 1; - strncat( m->headers[m->hdrcount], i, strstr(i, "\"") - i ); - debugmsg("parsed: %s", m->headers[m->hdrcount]); - m->hdrcount++; - } - res = 1; - } else - res = -1; - - return res; -} -- cgit From 891f648b677ad13183bf3852331bb76be8b7637a Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:01:30 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@36 f02b47b9-160a-0410-81a6-dc3441afb0ec --- README.csv | 7 ------- README.http | 24 ------------------------ README.standard | 15 --------------- README.xml | 21 --------------------- doc/README.csv | 7 +++++++ doc/README.http | 24 ++++++++++++++++++++++++ doc/README.standard | 15 +++++++++++++++ doc/README.xml | 21 +++++++++++++++++++++ 8 files changed, 67 insertions(+), 67 deletions(-) delete mode 100644 README.csv delete mode 100644 README.http delete mode 100644 README.standard delete mode 100644 README.xml create mode 100644 doc/README.csv create mode 100644 doc/README.http create mode 100644 doc/README.standard create mode 100644 doc/README.xml diff --git a/README.csv b/README.csv deleted file mode 100644 index 5f77c09..0000000 --- a/README.csv +++ /dev/null @@ -1,7 +0,0 @@ -This I/O Handler was put together primarily as a demonstration of -how easy it is to write handler routines for astmanproxy. - -However, it could be handy if you need CSV output, too. :) There -is no _read routine at this time; I would welcome someone to write -one and submit it to the project! - diff --git a/README.http b/README.http deleted file mode 100644 index 63268a9..0000000 --- a/README.http +++ /dev/null @@ -1,24 +0,0 @@ -This provides a very basic interface to http; it should be considered -poorly built, lame, and dangerous until further notice. - -That said, if you GET or POST form data name/value pairs to the proxy port -using HTTP, the proxy will treat it as Name: Value input and will act on -it, and will respond back in whatever format you want to use. - -A useful scenario might be to use http input and xml output. - -The following form can be used as a test: - - -
-Action:
-ActionID:
-
-
- - -See samples/httpast.html for a sample file that implements this. - -It would be best to use this with autofilter=on since right now you are -only going to get one response block back, and it might as well be -relevant. :) diff --git a/README.standard b/README.standard deleted file mode 100644 index 40699d7..0000000 --- a/README.standard +++ /dev/null @@ -1,15 +0,0 @@ -This is the standard input/output handler. This handler implements the -traditional Asterisk Manager interface I/O format, which can be -described as follows: - -Each block of input and output should be described in terms of headers, -each terminated with \r\n (cr+lf) and consisting of a name/value pair, -and each block of headers should be terminated with two \r\n sequences: - -Name1: Value1\r\nName2: Value2\r\n\r\n\r\n - -The "standard" IO handler is used by astmanproxy to communicate with -Asterisk server sessions. Clients connecting to astmanproxy can use -the "standard" handler if they wish, for either input, output or both, -or may use any other available handler. - diff --git a/README.xml b/README.xml deleted file mode 100644 index 0e34c97..0000000 --- a/README.xml +++ /dev/null @@ -1,21 +0,0 @@ -The XML input handler is homegrown and very basic at this point. The -following represents valid XML input: - - - - - - - - - - - -It's entirely possible that other formats will break the parser entirely. -I am toying with going with a lightweight XML parser like MiniXML that may -give some more flexibility at the price of having a dependency. It is -very likely, though, that the current parser will work fine for most -applications. - -DCT 6/23/2005 - diff --git a/doc/README.csv b/doc/README.csv new file mode 100644 index 0000000..5f77c09 --- /dev/null +++ b/doc/README.csv @@ -0,0 +1,7 @@ +This I/O Handler was put together primarily as a demonstration of +how easy it is to write handler routines for astmanproxy. + +However, it could be handy if you need CSV output, too. :) There +is no _read routine at this time; I would welcome someone to write +one and submit it to the project! + diff --git a/doc/README.http b/doc/README.http new file mode 100644 index 0000000..63268a9 --- /dev/null +++ b/doc/README.http @@ -0,0 +1,24 @@ +This provides a very basic interface to http; it should be considered +poorly built, lame, and dangerous until further notice. + +That said, if you GET or POST form data name/value pairs to the proxy port +using HTTP, the proxy will treat it as Name: Value input and will act on +it, and will respond back in whatever format you want to use. + +A useful scenario might be to use http input and xml output. + +The following form can be used as a test: + + +
+Action:
+ActionID:
+
+
+ + +See samples/httpast.html for a sample file that implements this. + +It would be best to use this with autofilter=on since right now you are +only going to get one response block back, and it might as well be +relevant. :) diff --git a/doc/README.standard b/doc/README.standard new file mode 100644 index 0000000..40699d7 --- /dev/null +++ b/doc/README.standard @@ -0,0 +1,15 @@ +This is the standard input/output handler. This handler implements the +traditional Asterisk Manager interface I/O format, which can be +described as follows: + +Each block of input and output should be described in terms of headers, +each terminated with \r\n (cr+lf) and consisting of a name/value pair, +and each block of headers should be terminated with two \r\n sequences: + +Name1: Value1\r\nName2: Value2\r\n\r\n\r\n + +The "standard" IO handler is used by astmanproxy to communicate with +Asterisk server sessions. Clients connecting to astmanproxy can use +the "standard" handler if they wish, for either input, output or both, +or may use any other available handler. + diff --git a/doc/README.xml b/doc/README.xml new file mode 100644 index 0000000..0e34c97 --- /dev/null +++ b/doc/README.xml @@ -0,0 +1,21 @@ +The XML input handler is homegrown and very basic at this point. The +following represents valid XML input: + + + + + + + + + + + +It's entirely possible that other formats will break the parser entirely. +I am toying with going with a lightweight XML parser like MiniXML that may +give some more flexibility at the price of having a dependency. It is +very likely, though, that the current parser will work fine for most +applications. + +DCT 6/23/2005 + -- cgit From 420c313f6c548845f248e98718e0b9396f015e98 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:03:30 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@37 f02b47b9-160a-0410-81a6-dc3441afb0ec --- TODO | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/TODO b/TODO index be076d0..decd317 100644 --- a/TODO +++ b/TODO @@ -3,13 +3,9 @@ Check for module versions; do not run without modules installed use a key? see loader.c in * State maintenance modules code formatting -tcpwrappers/access control/connect mask -tls? -better dir structure in source tree -subversion with digium +tcpwrappers/access control/connect mask? libtool/autoconf/automake support SetInputFormat proxyaction? -detect old config file fmt and puke kill http clients better? snmp? -- cgit From bbdd8a0682749bab14f511ca79c439d4681e8da3 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:05:44 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@38 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/include/astmanproxy.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index f5718aa..9bc103e 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -40,6 +40,7 @@ struct ast_server { char ast_user[80]; char ast_pass[80]; char ast_events[10]; + char use_ssl[5]; int status; /* TODO: have this mean something */ struct ast_server *next; }; @@ -67,6 +68,8 @@ struct proxyconfig { char logfile[80]; int retryinterval; int maxretries; + int asteriskwritetimeout; /* ms to wait when writing to asteriskfor ast_carefulwrite */ + int clientwritetimeout; /* ms to wait when writing to client ast_carefulwrite */ }; struct iohandler { @@ -95,6 +98,12 @@ struct mansession { struct ast_server *server; struct proxy_user user; char actionid[MAX_LEN]; + + /*! Authentication challenge */ + char challenge[10]; + + /* Timeout for ast_carefulwrite() */ + int writetimeout; struct mansession *next; }; @@ -123,3 +132,5 @@ int get_input(struct mansession *s, char *output); int SetIOHandlers(struct mansession *s, char *ifmt, char *ofmt); void destroy_session(struct mansession *s); int connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec); +int ast_carefulwrite(int fd, char *s, int len, int timeoutms); +extern void *SendError(struct mansession *s, char *errmsg); -- cgit From aa8304c0664b780dd47cb2a662664f897a8e7c34 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:07:59 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@39 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/astmanproxy.c | 3 +-- src/proxyfunc.c | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 6c82594..c24fb56 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -18,7 +18,6 @@ extern void *proxyaction_do(char *proxyaction, struct message *m, struct mansess extern void *ProxyLogin(struct mansession *s, char* username, char* secret); extern void *ProxyLogoff(struct mansession *s); extern int ValidateAction(struct message *m, struct mansession *s, int inbound); -extern void *SendError(struct mansession *s); int ConnectAsterisk(struct mansession *s); @@ -286,7 +285,7 @@ void *session_do(struct mansession *s) if ( !WriteAsterisk(&m) ) break; } else { - SendError(s); + SendError(s, "Action Filtered"); } } else if (res < 0) break; diff --git a/src/proxyfunc.c b/src/proxyfunc.c index 51ed5c6..99d6dbc 100644 --- a/src/proxyfunc.c +++ b/src/proxyfunc.c @@ -115,9 +115,7 @@ void *ProxyLogin(struct mansession *s, char *user, char *secret) { pthread_mutex_unlock(&userslock); if( !pu ) { - AddHeader(&m, "Response: Error"); - AddHeader(&m, "Message: Authentication failed"); - s->output->write(s, &m); + SendError(s, "Authentication failed"); s->authenticated = 0; if( debug ) debugmsg("Login failed as: %s/%s", user, secret); @@ -322,12 +320,12 @@ int ValidateAction(struct message *m, struct mansession *s, int inbound) { return 1; } -void *SendError(struct mansession *s) { +void *SendError(struct mansession *s, char *errmsg) { struct message m; memset(&m, 0, sizeof(struct message)); AddHeader(&m, "Response: Error"); - AddHeader(&m, "Message: Action Filtered"); + AddHeader(&m, "Message: %s", errmsg); s->output->write(s, &m); -- cgit From d42357d54b2c933a95ac637c9c1e521b3dbf0a16 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:12:46 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@40 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/include/ssl.h | 91 ++++++++++++++++ src/ssl.c | 313 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 404 insertions(+) create mode 100644 src/include/ssl.h create mode 100644 src/ssl.c diff --git a/src/include/ssl.h b/src/include/ssl.h new file mode 100644 index 0000000..a52b424 --- /dev/null +++ b/src/include/ssl.h @@ -0,0 +1,91 @@ +/* + * ssl_addon: Encrypts the asterisk management interface + * + * Copyrights: + * Copyright (C) 2005-2006, Tello Corporation, Inc. + * + * Contributors: + * Remco Treffkorn(Architect) and Mahesh Karoshi + * + * This program is free software, distributed under the terms of + * the GNU Lesser (Library) General Public License + * + * Copyright on this file is disclaimed to Digium for inclusion in Asterisk + */ + +#ifndef _SSL_ADDON_H_ +#define _SSL_ADDON_H_ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \brief + This data structure holds the additional SSL data needed to use the ssl functions. + The negative fd is used as an index into this data structure (after processing). + Choose SEC_MAX to be impossibly large for the application. +*/ +#define SEC_MAX 8 +struct { + int fd; + SSL* ssl; +} sec_channel[SEC_MAX]; + +/*! \brief + this has to be called before any other function dealing with ssl. +*/ +int init_secure(char* certfile); + +/*! \brief + Returns the real fd, that is received from os, when we accept the connection. +*/ +int get_real_fd(int fd); + +/*! \brief + Returns the ssl structure from the fd. +*/ +SSL *get_ssl(int fd); + +/*! \brief + Returns the availabe security slot. This restricts the maximun number of security connection, + the asterisk server can have for AMI. +*/ +int sec_getslot(void); + +/*! \brief + Accepts the connection, if the security is enabled it returns the negative fd. -1 is flase, -2, -3 + etc are ssl connections. +*/ +int saccept(int s); + +/*! \brief + Sends the data over secured or unsecured connections. +*/ +int m_send(int fd, const void *data, size_t len); + + +/*! \brief + Receives the connection from either ssl or fd. +*/ +int m_recv(int s, void *buf, size_t len, int flags); + + +/*! \brief + Needs to be called instead of close() to close a socket. + It also closes the ssl meta connection. +*/ + +int close_sock(int socket); + +int errexit(char s[]); + +int is_encrypt_request(int sslclhellotimeout, int fd); +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/ssl.c b/src/ssl.c new file mode 100644 index 0000000..c912435 --- /dev/null +++ b/src/ssl.c @@ -0,0 +1,313 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006, Tello Corporation, Inc. + * + * Remco Treffkorn(Architect) and Mahesh Karoshi(Senior Software Developer) + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief SSL for The Asterisk Management Interface - AMI + * + * Channel Management and more + * + * \author Remco Treffkorn(Architect) and Mahesh Karoshi(Senior Software Developer) + * \ref amiconf + */ + +/*! \addtogroup Group_AMI AMI functions +*/ +/*! @{ + Doxygen group */ + +/*! \note We use negative file descriptors for secure channels. The file descriptor + -1 is reseved for errors. -2 to -... are secure file descriptors. 0 to ... + are regular file descriptors. + + NOTE: Commonly error checks for routines returning fd's are done with (value<0). + You must check for (value==-1) instead, since all other negative fd's now + are valid fd's. +*/ +#ifdef AMI_WITH_SSL +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asterisk.h" +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 15611 $") + +#include "asterisk/logger.h" +#include "asterisk/options.h" +#include "asterisk/config.h" +#include "asterisk/ssl_addon.h" + +SSL_CTX *sctx; +static long rec_bytes; +static long sent_bytes; +static int ssl_initialized; + + +/*! \brief this has to be called before any other function dealing with ssl. + Initializes all the ssl related stuff here. */ +int init_secure(char *certfile) +{ + SSL_METHOD *meth; + + SSLeay_add_ssl_algorithms(); + SSL_load_error_strings(); + + /* server init */ + meth = SSLv23_server_method(); + sctx = SSL_CTX_new(meth); + + if (!sctx) { + return errexit("Failed to create a server ssl context!"); + } + + if (SSL_CTX_use_certificate_file(sctx, certfile, SSL_FILETYPE_PEM) <= 0) { + return errexit("Failed to use the certificate file!"); + } + + if (SSL_CTX_use_PrivateKey_file(sctx, certfile, SSL_FILETYPE_PEM) <= 0) { + return errexit("Failed to use the key file!\n"); + } + + if (!SSL_CTX_check_private_key(sctx)) { + return errexit("Private key does not match the certificate public key"); + } + ssl_initialized = 1; + return 0; +} + +/*! \brief Takes the negative ssl fd and returns the positive fd recieved from the os. + * It goes through arrray of fixed maximum number of secured channels. +*/ +int get_real_fd(int fd) +{ + if (fd<-1) { + fd = -fd - 2; + if (fd>=0 && fd =0 && fd 0) + rec_bytes += ret; + + if (option_debug > 2) + ast_log(LOG_DEBUG, "Received data from SSL socket - %d\n", ret); + return ret; +} + + +/*! \brief + Needs to be called instead of close() to close a socket. + It also closes the SSL meta connection. +*/ + +int close_sock(int socket) +{ + int ret=0; + SSL* ssl = NULL; + + if (socket < -1) { + socket = - socket - 2; + + ssl = sec_channel[socket].ssl; + sec_channel[socket].ssl = NULL; + socket = sec_channel[socket].fd; + } + + ret= close(socket); + + if (ssl) + SSL_free (ssl); + + return(ret); +} + +/*! \brief This process cannot continue without fixing this error. +*/ +int errexit(char s[]) +{ + ast_log(LOG_ERROR, "SSL critical error: %s", s); + return -1; +} + +/*! \brief Checks whether the client is requesting an ssl encrypted connection or not. If its encrypted + * request we expect "Client Hello" in the beginning of the message and ssl version 2. + * This can be verified by checking buf[0x02], buf[0x03] and buf[0x04]. If the contents are + * 0x01, 0x00, 0x02, then its an ssl packet with content "Client Hello", "SSL version 2". + * For SSL version 3, we might need to check for 0x01, 0x00, 0x03. + * +*/ +int is_encrypt_request(int sslclhellotimeout, int fd) +{ + fd_set listeners; + struct timeval tv; + char buf[1024]; + int ready_fdescriptors; + int ret; + + tv.tv_sec = 0; + tv.tv_usec = sslclhellotimeout * 1000; + + FD_ZERO(&listeners); + FD_SET(fd, &listeners); + + ready_fdescriptors = select (fd + 1, &listeners, NULL, NULL, &tv); + + if (ready_fdescriptors < 0 ) { + ast_log(LOG_ERROR, "select returned error, This should not happen: \n"); + return 0; + } else if (ready_fdescriptors == 0) { + return 0; + } + ret = recv(fd, buf, 100, MSG_PEEK); + if(ret > 0) { + /* check for sslv3 or tls*/ + if ((buf[0x00] == 0x16) && (buf[0x01] == 0x03) && + /* for tls buf[0x02] = 0x01 and ssl v3 buf[0x02] = 0x02 */ + ((buf[0x02] == 0x00) || (buf[0x02] == 0x01))) { + if (option_debug > 1) + ast_log(LOG_DEBUG, "Received a SSL request\n"); + return 1; + /* check for sslv23_client_method */ + } else if ((buf[0x02] == 0x01) && (buf[0x03] == 0x03) && (buf[0x04] == 0x01)) { + if (option_debug > 1) + ast_log(LOG_DEBUG, "Received a SSL request for SSLv23_client_method()\n"); + return 1; + } + /* check for sslv2 and return -1 */ + else if ((buf[0x02] == 0x01) && (buf[0x03] == 0x00) && (buf[0x04] == 0x02)) { + return -1; + } + } + return 0; +} +#endif -- cgit From 8d71d312a9a49a1d81088d27d7d7c0fae9f2e2f2 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:14:02 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@41 f02b47b9-160a-0410-81a6-dc3441afb0ec --- astmanproxy.conf | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/astmanproxy.conf b/astmanproxy.conf index ec19621..fdf7d66 100644 --- a/astmanproxy.conf +++ b/astmanproxy.conf @@ -1,10 +1,12 @@ ; astmanproxy.conf ; Asterisk Manager Proxy Configuration Sample -; (C) 2005 David C. Troy +; (C) 2005-2006 David C. Troy ; List of asterisk host(s) you want to proxy -; host = ip_addr, port, user, secret, events -host = localhost, 5038, dave, moo, on +; host = ip_addr, port, user, secret, events, use_ssl +host = localhost, 5038, dave, moo, on, off +host = 192.168.1.173, 5038, dave, moo, on, on + ;host = 127.0.0.2, 5038, user, secret, on ;host = otherhost, 5038, user, secret, on ;host = newhost, 5030, user, secret, off @@ -19,6 +21,12 @@ retryinterval = 2 ; use 0 for infinitely, or some finite number maxretries = 10 +; Amount of time to wait before timing out on writes to asterisk +asteriskwritetimeout=100 + +; Amount of time to wait before timing out on writes to clients +clientwritetimeout=200 + ; Address for proxy to listen on, can be set to * or x.x.x.x format ; recommend that you listen only on 127.0.0.1 or on an interface that ; is otherwise locked down to a trusted host, since the proxy @@ -58,6 +66,6 @@ outputformat = standard autofilter = off ; location of logfile -- will be owned by proc_user/proc_group -;logfile = /var/log/asterisk/astmanproxy.log -logfile = /opt/log/asterisk/astmanproxy.log +;logfile = /opt/log/asterisk/astmanproxy.log +logfile = /var/log/asterisk/astmanproxy.log -- cgit From b71a2d9e1496477cbfd2b750ff48c50b65a19a75 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:15:48 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@42 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/config.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config.c b/src/config.c index bfb8d1f..50b0896 100644 --- a/src/config.c +++ b/src/config.c @@ -95,6 +95,10 @@ void *processline(char *s) { strcpy(pc.listen_addr, value); else if (!strcmp(name,"listenport") ) pc.listen_port = atoi(value); + else if (!strcmp(name,"asteriskwritetimeout") ) + pc.asteriskwritetimeout = atoi(value); + else if (!strcmp(name,"clientwritetimeout") ) + pc.clientwritetimeout = atoi(value); else if (!strcmp(name,"proxykey") ) strcpy(pc.key, value); else if (!strcmp(name,"proc_user") ) -- cgit From 66304bf4ed08647861e7f14f4afe45be7bdd20b8 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:16:57 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@43 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/include/md5.h | 18 ++++ src/md5.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 278 insertions(+) create mode 100644 src/include/md5.h create mode 100644 src/md5.c diff --git a/src/include/md5.h b/src/include/md5.h new file mode 100644 index 0000000..30ac30c --- /dev/null +++ b/src/include/md5.h @@ -0,0 +1,18 @@ +#ifndef MD5_H +#define MD5_H + +#include + +struct MD5Context { + uint32_t buf[4]; + uint32_t bits[2]; + unsigned char in[64]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(uint32_t buf[4], uint32_t const in[16]); + +#endif /* !MD5_H */ diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 0000000..38db447 --- /dev/null +++ b/src/md5.c @@ -0,0 +1,260 @@ +/* MD5 checksum routines used for authentication. Not covered by GPL, but + in the public domain as per the copyright below */ + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include "include/astmanproxy.h" +#include "include/endian.h" +#include "include/md5.h" + +# if __BYTE_ORDER == __BIG_ENDIAN +# define HIGHFIRST 1 +# endif +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +void byteReverse(unsigned char *buf, unsigned longs); + +#ifndef ASM_MD5 +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(unsigned char *buf, unsigned longs) +{ + uint32_t t; + do { + t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32_t *) buf = t; + buf += 4; + } while (--longs); +} +#endif +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + uint32_t t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32_t *) ctx->in)[14] = ctx->bits[0]; + ((uint32_t *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(uint32_t buf[4], uint32_t const in[16]) +{ + register uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif -- cgit From a2957dba6e5f8c607a13a66b2489c0c2f41d31f5 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:22:35 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@44 f02b47b9-160a-0410-81a6-dc3441afb0ec --- Makefile | 2 +- src/include/endian.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/md5.c | 6 +++--- src/proxyfunc.c | 31 +++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 src/include/endian.h diff --git a/Makefile b/Makefile index 7b62a04..02d80cb 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ PREFIX:= /usr/local BINDIR := $(DESTDIR)$(PREFIX)/sbin # For compilation dependencies -MODS := astmanproxy config config_perms common proxyfunc log +MODS := astmanproxy config config_perms common proxyfunc log ssl md5 HANDLERS := xml standard csv http SOBJS := $(HANDLERS:%=%.so) diff --git a/src/include/endian.h b/src/include/endian.h new file mode 100644 index 0000000..f5e20fb --- /dev/null +++ b/src/include/endian.h @@ -0,0 +1,60 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Asterisk architecture endianess compatibility definitions + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer + * + * This program is free software, distributed under the terms of + * the GNU Lesser General Public License. Other components of + * Asterisk are distributed under The GNU General Public License + * only. + */ + +#ifndef _ASTERISK_ENDIAN_H +#define _ASTERISK_ENDIAN_H + +/* + * Autodetect system endianess + */ + +#ifdef SOLARIS +#include "solaris-compat/compat.h" +#endif + +#ifndef __BYTE_ORDER +#ifdef __linux__ +#include +#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) +#if defined(__OpenBSD__) +#include +#endif /* __OpenBSD__ */ +#include +#define __BYTE_ORDER BYTE_ORDER +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN +#else +#ifdef __LITTLE_ENDIAN__ +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif /* __LITTLE_ENDIAN */ + +#if defined(i386) || defined(__i386__) +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif /* defined i386 */ + +#if defined(sun) && defined(unix) && defined(sparc) +#define __BYTE_ORDER __BIG_ENDIAN +#endif /* sun unix sparc */ + +#endif /* linux */ + +#endif /* __BYTE_ORDER */ + +#ifndef __BYTE_ORDER +#error Need to know endianess +#endif /* __BYTE_ORDER */ + +#endif /* _ASTERISK_ENDIAN_H */ + diff --git a/src/md5.c b/src/md5.c index 38db447..8d31833 100644 --- a/src/md5.c +++ b/src/md5.c @@ -18,9 +18,9 @@ * will fill a supplied 16-byte array with the digest. */ -#include "include/astmanproxy.h" -#include "include/endian.h" -#include "include/md5.h" +#include "astmanproxy.h" +#include "endian.h" +#include "md5.h" # if __BYTE_ORDER == __BIG_ENDIAN # define HIGHFIRST 1 diff --git a/src/proxyfunc.c b/src/proxyfunc.c index 99d6dbc..31094f8 100644 --- a/src/proxyfunc.c +++ b/src/proxyfunc.c @@ -332,3 +332,34 @@ void *SendError(struct mansession *s, char *errmsg) { return 0; } +/*! If you are calling ast_carefulwrite, it is assumed that you are calling + it on a file descriptor that _DOES_ have NONBLOCK set. This way, + there is only one system call made to do a write, unless we actually + have a need to wait. This way, we get better performance. */ +int ast_carefulwrite(int fd, char *s, int len, int timeoutms) +{ + /* Try to write string, but wait no more than ms milliseconds + before timing out */ + int res=0; + struct pollfd fds[1]; + while(len) { + res = m_send(fd, s, len); + if ((res < 0) && (errno != EAGAIN)) { + return -1; + } + if (res < 0) res = 0; + len -= res; + s += res; + res = 0; + if (len) { + fds[0].fd = get_real_fd(fd); + fds[0].events = POLLOUT; + /* Wait until writable again */ + res = poll(fds, 1, timeoutms); + if (res < 1) + return -1; + } + } + return res; +} + -- cgit From 7244c5ad9786e7bcab9eb44fe10e4f1b3df023dc Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:29:10 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@45 f02b47b9-160a-0410-81a6-dc3441afb0ec --- VERSIONS | 9 +++++++++ src/include/astmanproxy.h | 8 ++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/VERSIONS b/VERSIONS index 8a29a2b..4f048ce 100644 --- a/VERSIONS +++ b/VERSIONS @@ -42,3 +42,12 @@ Added support for Mac OS X (Tested on 10.3.9); BSD may also work Aborts on old config file format (detects incomplete server spec) +1.20pre Now using ast_carefulwrite for non-blocking writes + Added asteriskwritetimeout config setting for asterisk write timeout + Added clientwritetimeout config setting for client write timeout + Added 'usessl' option for connecting to asterisk servers + Added support for astmanproxy.users user authentication (Steve Davies) + Added support for Action: Challenge/AuthType: MD5 authentication + Added challenge field to mansession structure for use by Action: Challenge + Added writetimeout var to mansession structure for use by ast_carefulwrite + diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index 9bc103e..83dc1ba 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -98,12 +98,8 @@ struct mansession { struct ast_server *server; struct proxy_user user; char actionid[MAX_LEN]; - - /*! Authentication challenge */ - char challenge[10]; - - /* Timeout for ast_carefulwrite() */ - int writetimeout; + char challenge[10]; /*! Authentication challenge */ + int writetimeout; /* Timeout for ast_carefulwrite() */ struct mansession *next; }; -- cgit From 7800c64a9f385d4cd3092afb10bb94704ccb24e6 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:32:50 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@46 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/md5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/md5.c b/src/md5.c index 8d31833..cda3441 100644 --- a/src/md5.c +++ b/src/md5.c @@ -18,8 +18,8 @@ * will fill a supplied 16-byte array with the digest. */ -#include "astmanproxy.h" #include "endian.h" +#include "astmanproxy.h" #include "md5.h" # if __BYTE_ORDER == __BIG_ENDIAN -- cgit From b4846e8daffe14658826c638a843aea45606ca79 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:41:56 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@47 f02b47b9-160a-0410-81a6-dc3441afb0ec --- Makefile | 10 ++++++---- src/ssl.c | 23 +++++++++-------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 02d80cb..2350849 100644 --- a/Makefile +++ b/Makefile @@ -26,9 +26,10 @@ BINDIR := $(DESTDIR)$(PREFIX)/sbin MODS := astmanproxy config config_perms common proxyfunc log ssl md5 HANDLERS := xml standard csv http SOBJS := $(HANDLERS:%=%.so) +LIBS := ssl ifeq (${OSARCH},Darwin) - LIBS=-lresolv + LIBS+=-lresolv CFLAGS+=-D__Darwin_ BINDIR=/opt/sbin LIBDIR=/opt/lib/astmanproxy @@ -42,7 +43,7 @@ ifeq (${OSARCH},Darwin) SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace else #These are used for all but Darwin - LIBS=-ldl -pthread + LIBS+=-ldl -pthread ASTLINK=-Wl,-E SOLINK=-shared -Xlinker -x LOGDIR=/var/log/asterisk @@ -52,12 +53,13 @@ OBJS += $(MODS:%=%.o) MODDIR := $(LIBDIR)/modules CONF_TARGET:= $(CONFDIR_REAL)/$(CONFFILE) PERM_TARGET:= $(PERMDIR_REAL)/$(PERMFILE) -DEFINES:='-DPROXY_VERSION="$(VERSION)"' '-DCDIR="$(CONFDIR)"' '-DCFILE="$(CONFFILE)"' '-DMDIR="$(MODDIR)"' '-DPDIR="$(PERMDIR)"' '-DPFILE="$(PERMFILE)"' +DEFINES:=-DAMI_WITH_SSL '-DPROXY_VERSION="$(VERSION)"' '-DCDIR="$(CONFDIR)"' '-DCFILE="$(CONFFILE)"' +DEFINES+='-DMDIR="$(MODDIR)"' '-DPDIR="$(PERMDIR)"' '-DPFILE="$(PERMFILE)"' VPATH = src # Add -g below for debug/GDB symbols #CFLAGS+= $(DEFINES) -Wall -O2 -dynamic -D_REENTRANT -fPIC -CFLAGS+= $(DEFINES) -Wall -O2 -D_REENTRANT -fPIC -Isrc/include +CFLAGS+= $(DEFINES) -Wall -O2 -D_REENTRANT -fPIC -Isrc/include -I- # For printing only SRCS := $(MODS:%=%.c) diff --git a/src/ssl.c b/src/ssl.c index c912435..5570790 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -54,13 +54,8 @@ #include #include -#include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 15611 $") - -#include "asterisk/logger.h" -#include "asterisk/options.h" -#include "asterisk/config.h" -#include "asterisk/ssl_addon.h" +#include "astmanproxy.h" +#include "ssl.h" SSL_CTX *sctx; static long rec_bytes; @@ -174,7 +169,7 @@ int saccept(int s) } while( err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); - ast_log(LOG_DEBUG, "ssl_addon: Connection accepted"); + debugmsg("ssl_addon: Connection accepted"); err=1; @@ -219,8 +214,8 @@ int m_recv(int s, void *buf, size_t len, int flags) if (ret > 0) rec_bytes += ret; - if (option_debug > 2) - ast_log(LOG_DEBUG, "Received data from SSL socket - %d\n", ret); + if (debug) + debugmsg("Received data from SSL socket - %d\n", ret); return ret; } @@ -255,7 +250,7 @@ int close_sock(int socket) */ int errexit(char s[]) { - ast_log(LOG_ERROR, "SSL critical error: %s", s); + debugmsg("SSL critical error: %s", s); return -1; } @@ -283,7 +278,7 @@ int is_encrypt_request(int sslclhellotimeout, int fd) ready_fdescriptors = select (fd + 1, &listeners, NULL, NULL, &tv); if (ready_fdescriptors < 0 ) { - ast_log(LOG_ERROR, "select returned error, This should not happen: \n"); + debugmsg("select returned error, This should not happen: \n"); return 0; } else if (ready_fdescriptors == 0) { return 0; @@ -295,12 +290,12 @@ int is_encrypt_request(int sslclhellotimeout, int fd) /* for tls buf[0x02] = 0x01 and ssl v3 buf[0x02] = 0x02 */ ((buf[0x02] == 0x00) || (buf[0x02] == 0x01))) { if (option_debug > 1) - ast_log(LOG_DEBUG, "Received a SSL request\n"); + debugmsg("Received a SSL request\n"); return 1; /* check for sslv23_client_method */ } else if ((buf[0x02] == 0x01) && (buf[0x03] == 0x03) && (buf[0x04] == 0x01)) { if (option_debug > 1) - ast_log(LOG_DEBUG, "Received a SSL request for SSLv23_client_method()\n"); + debugmsg("Received a SSL request for SSLv23_client_method()\n"); return 1; } /* check for sslv2 and return -1 */ -- cgit From 268eacb1fa13470f22d6bcea81e9403d3824e8cb Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:47:13 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@48 f02b47b9-160a-0410-81a6-dc3441afb0ec --- Makefile | 4 ++-- src/ssl.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 2350849..37da0d4 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ BINDIR := $(DESTDIR)$(PREFIX)/sbin MODS := astmanproxy config config_perms common proxyfunc log ssl md5 HANDLERS := xml standard csv http SOBJS := $(HANDLERS:%=%.so) -LIBS := ssl +LIBS := -lssl ifeq (${OSARCH},Darwin) LIBS+=-lresolv @@ -59,7 +59,7 @@ VPATH = src # Add -g below for debug/GDB symbols #CFLAGS+= $(DEFINES) -Wall -O2 -dynamic -D_REENTRANT -fPIC -CFLAGS+= $(DEFINES) -Wall -O2 -D_REENTRANT -fPIC -Isrc/include -I- +CFLAGS+= $(DEFINES) -Wall -O2 -D_REENTRANT -fPIC -Isrc/include -I/usr/include/openssl -I- # For printing only SRCS := $(MODS:%=%.c) diff --git a/src/ssl.c b/src/ssl.c index 5570790..7ff4ab5 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -289,12 +289,12 @@ int is_encrypt_request(int sslclhellotimeout, int fd) if ((buf[0x00] == 0x16) && (buf[0x01] == 0x03) && /* for tls buf[0x02] = 0x01 and ssl v3 buf[0x02] = 0x02 */ ((buf[0x02] == 0x00) || (buf[0x02] == 0x01))) { - if (option_debug > 1) + if (debug) debugmsg("Received a SSL request\n"); return 1; /* check for sslv23_client_method */ } else if ((buf[0x02] == 0x01) && (buf[0x03] == 0x03) && (buf[0x04] == 0x01)) { - if (option_debug > 1) + if (debug) debugmsg("Received a SSL request for SSLv23_client_method()\n"); return 1; } -- cgit From 291633f8cb612e222fc6a8209ebbbd6904bcf3de Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:49:34 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@49 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/astmanproxy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/astmanproxy.c b/src/astmanproxy.c index c24fb56..065c05a 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -225,10 +225,10 @@ int WriteAsterisk(struct message *m) { for (i=0; ihdrcount; i++) { if (strcasecmp(m->headers[i], "Server:") ) { sprintf(outstring, "%s\r\n", m->headers[i]); - write(s->fd, outstring, strlen(outstring) ); + ast_carefulwrite(s->fd, outstring, strlen(outstring), s->writetimeout ); } } - write(s->fd, "\r\n", 2); + ast_carefulwrite(s->fd, "\r\n", 2, s->writetimeout); pthread_mutex_unlock(&s->lock); return 1; } -- cgit From 003236f73567a285b35fc8d571ac94fb0bc51278 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:55:39 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@50 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/config.c | 4 ++++ src/csv.c | 4 ++-- src/standard.c | 12 ++++-------- src/xml.c | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/config.c b/src/config.c index 50b0896..2ffdd1a 100644 --- a/src/config.c +++ b/src/config.c @@ -226,6 +226,10 @@ int ReadConfig() { fclose(FP); + if (!pc.asteriskwritetimeout) + pc.asteriskwritetimeout = 100; + if (!pc.clientwritetimeout) + pc.clientwritetimeout = 100; return 0; } diff --git a/src/csv.c b/src/csv.c index bf8e800..fb26f3b 100644 --- a/src/csv.c +++ b/src/csv.c @@ -20,9 +20,9 @@ int _write(struct mansession *s, struct message *m) { sprintf(outstring, "\"%s\"", m->headers[i]); if (ihdrcount-1) strcat(outstring, ", "); - write(s->fd, outstring, strlen(outstring)); + ast_carefulwrite(s->fd, outstring, strlen(outstring), s->writetimeout); } - write(s->fd, "\r\n\r\n", 4); + ast_carefulwrite(s->fd, "\r\n\r\n", 4, s->writetimeout); pthread_mutex_unlock(&s->lock); return 0; diff --git a/src/standard.c b/src/standard.c index 5933e45..26cd4a8 100644 --- a/src/standard.c +++ b/src/standard.c @@ -49,14 +49,10 @@ int _write(struct mansession *s, struct message *m) { if (debug) debugmsg("in standard_write module..."); pthread_mutex_lock(&s->lock); for (i=0; ihdrcount; i++) { - if( strncasecmp( m->headers[i], "CallerID: ", 19 ) == 0 ) { - write(s->fd, "CallerID: ", 10); - } else { - write(s->fd, m->headers[i], strlen(m->headers[i]) ); - } - write(s->fd, "\r\n", 2); + ast_carefulwrite(s->fd, m->headers[i], strlen(m->headers[i]) , s->writetimeout); + ast_carefulwrite(s->fd, "\r\n", 2, s->writetimeout); } - write(s->fd, "\r\n", 2); + ast_carefulwrite(s->fd, "\r\n", 2, s->writetimeout); pthread_mutex_unlock(&s->lock); return 0; @@ -68,7 +64,7 @@ int _onconnect(struct mansession *s, struct message *m) { sprintf(banner, "%s/%s\r\n", PROXY_BANNER, PROXY_VERSION); pthread_mutex_lock(&s->lock); - write(s->fd, banner, strlen(banner)); + ast_carefulwrite(s->fd, banner, strlen(banner), s->writetimeout); pthread_mutex_unlock(&s->lock); return 0; diff --git a/src/xml.c b/src/xml.c index 0d6737b..2f3f9d0 100644 --- a/src/xml.c +++ b/src/xml.c @@ -74,7 +74,7 @@ int _write(struct mansession *s, struct message *m) { sprintf(buf, "<%s>\r\n", xmldoctag); pthread_mutex_lock(&s->lock); - write(s->fd, buf, strlen(buf)); + ast_carefulwrite(s->fd, buf, strlen(buf), s->writetimeout); for (i=0; ihdrcount; i++) { memset(xmlescaped, 0, sizeof xmlescaped); @@ -89,10 +89,10 @@ int _write(struct mansession *s, struct message *m) { strcat(outstring, "\"/>\r\n"); } else sprintf(outstring, " <%s Value=\"%s\"/>\r\n", XML_UNPARSED, lpos); - write(s->fd, outstring, strlen(outstring) ); + ast_carefulwrite(s->fd, outstring, strlen(outstring), s->writetimeout); } sprintf(buf, "\r\n\r\n", xmldoctag); - write(s->fd, buf, strlen(buf)); + ast_carefulwrite(s->fd, buf, strlen(buf), s->writetimeout); pthread_mutex_unlock(&s->lock); return 0; -- cgit From f723379bb0de062ba8203e0dba098b146075fb15 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 17:56:53 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@51 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http.c b/src/http.c index 6b5580d..b91b872 100644 --- a/src/http.c +++ b/src/http.c @@ -102,7 +102,7 @@ int _read(struct mansession *s, struct message *m) { BuildHTTPHeader(header); pthread_mutex_lock(&s->lock); s->inputcomplete = 1; - write(s->fd, header, strlen(header)); + ast_carefulwrite(s->fd, header, strlen(header), s->writetimeout); pthread_mutex_unlock(&s->lock); debugmsg("header: %s", header); -- cgit From cf834d379b5a6a7393d0c25386c55959f5df4550 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 18:07:37 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@52 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/astmanproxy.c | 2 ++ src/proxyfunc.c | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 065c05a..31af40d 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -277,6 +277,8 @@ void *session_do(struct mansession *s) ProxyLogin(s, astman_get_header(&m, "UserName"), astman_get_header(&m, "Secret")); else if ( !strcasecmp(action, "Logoff") ) ProxyLogoff(s); + else if ( !strcasecmp(action, "Challenge") ) + ProxyChallenge(s, &m); else if ( !(*proxyaction == '\0') ) proxyaction_do(proxyaction, &m, s); else if ( ValidateAction(&m, s ,0) ) { diff --git a/src/proxyfunc.c b/src/proxyfunc.c index 31094f8..ed4770e 100644 --- a/src/proxyfunc.c +++ b/src/proxyfunc.c @@ -64,6 +64,25 @@ void *ProxySetOutputFormat(struct mansession *s, struct message *m) { return 0; } +int ProxyChallenge(struct mansession *s, struct message *m) { + struct message mo; + + if ( strcasecmp("MD5", astman_get_header(m, "AuthType")) ) { + SendError(s, "Must specify AuthType"); + return 1; + } + + if (!*s->challenge) + snprintf(s->challenge, sizeof(s->challenge), "%d", rand()); + + memset(&mo, 0, sizeof(struct message)); + AddHeader(&mo, "Response: Success"); + AddHeader(&mo, "Challenge: %d", s->challenge); + + s->output->write(s, &mo); + return 0; +} + void *ProxySetAutoFilter(struct mansession *s, struct message *m) { struct message mo; char *value; -- cgit From e7575b72bd6cd202c6a9b93369d86bf7c7974938 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 18:52:17 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@53 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/astmanproxy.c | 4 ++-- src/proxyfunc.c | 17 +++++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 31af40d..bb3d260 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -15,7 +15,7 @@ extern FILE *OpenLogfile( void ); extern int SetProcUID( void ); extern void *proxyaction_do(char *proxyaction, struct message *m, struct mansession *s); -extern void *ProxyLogin(struct mansession *s, char* username, char* secret); +extern void *ProxyLogin(struct mansession *s, struct message *m); extern void *ProxyLogoff(struct mansession *s); extern int ValidateAction(struct message *m, struct mansession *s, int inbound); @@ -274,7 +274,7 @@ void *session_do(struct mansession *s) actionid = astman_get_header(&m, ACTION_ID); action = astman_get_header(&m, "Action"); if ( !strcasecmp(action, "Login") ) - ProxyLogin(s, astman_get_header(&m, "UserName"), astman_get_header(&m, "Secret")); + ProxyLogin(s, &m); else if ( !strcasecmp(action, "Logoff") ) ProxyLogoff(s); else if ( !strcasecmp(action, "Challenge") ) diff --git a/src/proxyfunc.c b/src/proxyfunc.c index ed4770e..54b7789 100644 --- a/src/proxyfunc.c +++ b/src/proxyfunc.c @@ -106,11 +106,16 @@ void *ProxySetAutoFilter(struct mansession *s, struct message *m) { return 0; } -void *ProxyLogin(struct mansession *s, char *user, char *secret) { - struct message m; +void *ProxyLogin(struct mansession *s, struct message *m) { + struct message mo; struct proxy_user *pu; + char *user, *secret, *md5key; - memset(&m, 0, sizeof(struct message)); + user = astman_get_header(m, "Username"); + secret = astman_get_header(m, "Secret"); + md5key = astman_get_header(m, "Key"); + + memset(&mo, 0, sizeof(struct message)); if( debug ) debugmsg("Login attempt as: %s/%s", user, secret); @@ -118,9 +123,9 @@ void *ProxyLogin(struct mansession *s, char *user, char *secret) { pu = pc.userlist; while( pu ) { if ( !strcmp(user, pu->username) && !strcmp(secret, pu->secret) ) { - AddHeader(&m, "Response: Success"); - AddHeader(&m, "Message: Authentication accepted"); - s->output->write(s, &m); + AddHeader(&mo, "Response: Success"); + AddHeader(&mo, "Message: Authentication accepted"); + s->output->write(s, &mo); s->authenticated = 1; strcpy(s->user.channel, pu->channel); strcpy(s->user.icontext, pu->icontext); -- cgit From fe3ac9141e8cc4caf966b64d8c370f818436d79f Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 19:14:17 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@54 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/proxyfunc.c | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/proxyfunc.c b/src/proxyfunc.c index 54b7789..95257c2 100644 --- a/src/proxyfunc.c +++ b/src/proxyfunc.c @@ -1,4 +1,5 @@ #include "astmanproxy.h" +#include "md5.h" extern struct mansession *sessions; extern struct iohandler *iohandlers; @@ -106,6 +107,28 @@ void *ProxySetAutoFilter(struct mansession *s, struct message *m) { return 0; } +int AuthMD5(char *key, char *challenge, char *password) { + int x; + int len=0; + char md5key[256] = ""; + struct MD5Context md5; + unsigned char digest[16]; + + if (!*challenge || !*password || !*key) + return 1; + + MD5Init(&md5); + MD5Update(&md5, (unsigned char *) challenge, strlen(challenge)); + MD5Update(&md5, (unsigned char *) password, strlen(password)); + MD5Final(digest, &md5); + for (x=0;x<16;x++) + len += sprintf(md5key + len, "%2.2x", digest[x]); + if (!strcmp(md5key, key)) + return 0; + + return 1; +} + void *ProxyLogin(struct mansession *s, struct message *m) { struct message mo; struct proxy_user *pu; @@ -122,17 +145,20 @@ void *ProxyLogin(struct mansession *s, struct message *m) { pthread_mutex_lock(&userslock); pu = pc.userlist; while( pu ) { - if ( !strcmp(user, pu->username) && !strcmp(secret, pu->secret) ) { - AddHeader(&mo, "Response: Success"); - AddHeader(&mo, "Message: Authentication accepted"); - s->output->write(s, &mo); - s->authenticated = 1; - strcpy(s->user.channel, pu->channel); - strcpy(s->user.icontext, pu->icontext); - strcpy(s->user.ocontext, pu->ocontext); - if( debug ) - debugmsg("Login as: %s", user); - break; + if ( !strcmp(user, pu->username) ) { + if (!AuthMD5(md5key, s->challenge, pu->secret) || + !strcmp(secret, pu->secret) ) { + AddHeader(&mo, "Response: Success"); + AddHeader(&mo, "Message: Authentication accepted"); + s->output->write(s, &mo); + s->authenticated = 1; + strcpy(s->user.channel, pu->channel); + strcpy(s->user.icontext, pu->icontext); + strcpy(s->user.ocontext, pu->ocontext); + if( debug ) + debugmsg("Login as: %s", user); + break; + } } pu = pu->next; } -- cgit From 0785a24392bc993ceb36b9450b4ca720caf25531 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 19:58:09 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@55 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/proxyfunc.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/proxyfunc.c b/src/proxyfunc.c index 95257c2..2992267 100644 --- a/src/proxyfunc.c +++ b/src/proxyfunc.c @@ -78,7 +78,7 @@ int ProxyChallenge(struct mansession *s, struct message *m) { memset(&mo, 0, sizeof(struct message)); AddHeader(&mo, "Response: Success"); - AddHeader(&mo, "Challenge: %d", s->challenge); + AddHeader(&mo, "Challenge: %s", s->challenge); s->output->write(s, &mo); return 0; @@ -114,29 +114,35 @@ int AuthMD5(char *key, char *challenge, char *password) { struct MD5Context md5; unsigned char digest[16]; - if (!*challenge || !*password || !*key) + if (!*key || !*challenge || !*password ) return 1; + if (debug) + debugmsg("MD5 password=%s, challenge=%s", password, challenge); + MD5Init(&md5); MD5Update(&md5, (unsigned char *) challenge, strlen(challenge)); MD5Update(&md5, (unsigned char *) password, strlen(password)); MD5Final(digest, &md5); for (x=0;x<16;x++) len += sprintf(md5key + len, "%2.2x", digest[x]); + if( debug ) { + debugmsg("MD5 computed=%s, received=%s", md5key, key); + } if (!strcmp(md5key, key)) return 0; - - return 1; + else + return 1; } void *ProxyLogin(struct mansession *s, struct message *m) { struct message mo; struct proxy_user *pu; - char *user, *secret, *md5key; + char *user, *secret, *key; user = astman_get_header(m, "Username"); secret = astman_get_header(m, "Secret"); - md5key = astman_get_header(m, "Key"); + key = astman_get_header(m, "Key"); memset(&mo, 0, sizeof(struct message)); if( debug ) @@ -146,7 +152,7 @@ void *ProxyLogin(struct mansession *s, struct message *m) { pu = pc.userlist; while( pu ) { if ( !strcmp(user, pu->username) ) { - if (!AuthMD5(md5key, s->challenge, pu->secret) || + if (!AuthMD5(key, s->challenge, pu->secret) || !strcmp(secret, pu->secret) ) { AddHeader(&mo, "Response: Success"); AddHeader(&mo, "Message: Authentication accepted"); -- cgit From 8bdd2c1273d4ca4dd576f71ad5bae9bb7b38c5d5 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 1 Apr 2006 20:05:38 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@56 f02b47b9-160a-0410-81a6-dc3441afb0ec --- INSTALL | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 INSTALL diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..d50a2c7 --- /dev/null +++ b/INSTALL @@ -0,0 +1,13 @@ +Quick Install Instructions +-------------------------- + +It's easy: + +1. make +2. make install +3. run: + +astmanproxy -d (for debug mode) +astmanproxy (for background mode) + +Enjoy! -- cgit From 55a533edf2e466a34179e12bf36e239902aad5fc Mon Sep 17 00:00:00 2001 From: David Troy Date: Sun, 2 Apr 2006 16:24:56 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@57 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/common.c | 32 ++++++++++++++++++++++++++++++++ src/proxyfunc.c | 32 -------------------------------- src/ssl.c | 4 ++-- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/common.c b/src/common.c index 5f63c15..8da909c 100644 --- a/src/common.c +++ b/src/common.c @@ -145,3 +145,35 @@ done: } return(0); } + +/*! If you are calling ast_carefulwrite, it is assumed that you are calling + it on a file descriptor that _DOES_ have NONBLOCK set. This way, + there is only one system call made to do a write, unless we actually + have a need to wait. This way, we get better performance. */ +int ast_carefulwrite(int fd, char *s, int len, int timeoutms) +{ + /* Try to write string, but wait no more than ms milliseconds + before timing out */ + int res=0; + struct pollfd fds[1]; + while(len) { + res = m_send(fd, s, len); + if ((res < 0) && (errno != EAGAIN)) { + return -1; + } + if (res < 0) res = 0; + len -= res; + s += res; + res = 0; + if (len) { + fds[0].fd = get_real_fd(fd); + fds[0].events = POLLOUT; + /* Wait until writable again */ + res = poll(fds, 1, timeoutms); + if (res < 1) + return -1; + } + } + return res; +} + diff --git a/src/proxyfunc.c b/src/proxyfunc.c index 2992267..54df298 100644 --- a/src/proxyfunc.c +++ b/src/proxyfunc.c @@ -387,35 +387,3 @@ void *SendError(struct mansession *s, char *errmsg) { return 0; } - -/*! If you are calling ast_carefulwrite, it is assumed that you are calling - it on a file descriptor that _DOES_ have NONBLOCK set. This way, - there is only one system call made to do a write, unless we actually - have a need to wait. This way, we get better performance. */ -int ast_carefulwrite(int fd, char *s, int len, int timeoutms) -{ - /* Try to write string, but wait no more than ms milliseconds - before timing out */ - int res=0; - struct pollfd fds[1]; - while(len) { - res = m_send(fd, s, len); - if ((res < 0) && (errno != EAGAIN)) { - return -1; - } - if (res < 0) res = 0; - len -= res; - s += res; - res = 0; - if (len) { - fds[0].fd = get_real_fd(fd); - fds[0].events = POLLOUT; - /* Wait until writable again */ - res = poll(fds, 1, timeoutms); - if (res < 1) - return -1; - } - } - return res; -} - diff --git a/src/ssl.c b/src/ssl.c index 7ff4ab5..b884eac 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -142,8 +142,8 @@ int sec_getslot(void) return i; } -/*! \brief Accepts the ssl connection. Retrurns the negative fd. negative fd's are - * choosen to differentiate between ssl and non-ssl connections. positive +/*! \brief Accepts the ssl connection. Returns the negative fd. negative fd's are + * chosen to differentiate between ssl and non-ssl connections. Positive * fd's are used for non-ssl connections and negative fd's are used for ssl * connections. So we purposefully calculate and return negative fds. * You can always get positive fd by calling get_real_fd(negative fd). -- cgit From 08192d2b732eaaff203012c0acda210b40261885 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sun, 2 Apr 2006 16:43:05 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@58 f02b47b9-160a-0410-81a6-dc3441afb0ec --- astmanproxy.conf | 9 +++++++++ src/astmanproxy.c | 2 +- src/common.c | 8 ++++++-- src/config.c | 16 ++++++++++++---- src/include/astmanproxy.h | 3 +++ 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/astmanproxy.conf b/astmanproxy.conf index fdf7d66..b8ab5d3 100644 --- a/astmanproxy.conf +++ b/astmanproxy.conf @@ -21,6 +21,15 @@ retryinterval = 2 ; use 0 for infinitely, or some finite number maxretries = 10 +; How long do we wait on the manager port for an SSL session start? (ms) +sslclienthellotimeout = 200 + +; Do we accept encrypted SSL manager connections? +acceptencryptedconnection = yes + +; Do we accept unencrypted manager connections? +acceptunencryptedconnection = no + ; Amount of time to wait before timing out on writes to asterisk asteriskwritetimeout=100 diff --git a/src/astmanproxy.c b/src/astmanproxy.c index bb3d260..0a3a172 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -544,7 +544,7 @@ static void *accept_thread() /* For safety, make sure socket is non-blocking */ flags = fcntl(as, F_GETFL); - fcntl(as, F_SETFL, flags | O_NONBLOCK); + fcntl(get_real_fd(as), F_SETFL, flags | O_NONBLOCK); pthread_mutex_init(&s->lock, NULL); s->fd = as; diff --git a/src/common.c b/src/common.c index 8da909c..b52ee59 100644 --- a/src/common.c +++ b/src/common.c @@ -37,7 +37,9 @@ int get_input(struct mansession *s, char *output) s->inlen = 0; } debugmsg("attempting poll operation"); - fds[0].fd = s->fd; + /* get actual fd, even if a negative SSL fd */ + fds[0].fd = get_real_fd(s->fd); + fds[0].events = POLLIN; res = poll(fds, 1, -1); debugmsg("returned from poll op"); @@ -46,7 +48,9 @@ int get_input(struct mansession *s, char *output) } else if (res > 0) { pthread_mutex_lock(&s->lock); debugmsg("attempting socket read in get_input..."); - res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen); + + /* read from socket; SSL or otherwise */ + res = m_recv(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen, 0); pthread_mutex_unlock(&s->lock); if (res < 1) return -1; diff --git a/src/config.c b/src/config.c index 2ffdd1a..824043b 100644 --- a/src/config.c +++ b/src/config.c @@ -99,6 +99,12 @@ void *processline(char *s) { pc.asteriskwritetimeout = atoi(value); else if (!strcmp(name,"clientwritetimeout") ) pc.clientwritetimeout = atoi(value); + else if (!strcmp(name,"sslclienthellotimeout") ) + pc.sslclhellotimeout = atoi(value); + else if (!strcmp(name,"acceptencryptedconnection") ) + pc.acceptencryptedconnection = strcmp(value,"yes") ? 0 : 1; + else if (!strcmp(name,"acceptunencryptedconnection") ) + pc.acceptunencryptedconnection = strcmp(value,"yes") ? 0 : 1; else if (!strcmp(name,"proxykey") ) strcpy(pc.key, value); else if (!strcmp(name,"proc_user") ) @@ -207,6 +213,12 @@ int ReadConfig() { memset( &pc, 0, sizeof pc ); + + /* Set nonzero config defaults */ + pc.asteriskwritetimeout = 100; + pc.clientwritetimeout = 100; + pc.sslclhellotimeout = 200; + sprintf(cfn, "%s/%s", CDIR, CFILE); FP = fopen( cfn, "r" ); @@ -226,10 +238,6 @@ int ReadConfig() { fclose(FP); - if (!pc.asteriskwritetimeout) - pc.asteriskwritetimeout = 100; - if (!pc.clientwritetimeout) - pc.clientwritetimeout = 100; return 0; } diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index 83dc1ba..fdd783d 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -70,6 +70,9 @@ struct proxyconfig { int maxretries; int asteriskwritetimeout; /* ms to wait when writing to asteriskfor ast_carefulwrite */ int clientwritetimeout; /* ms to wait when writing to client ast_carefulwrite */ + int sslclhellotimeout; /* ssl client hello timeout -- how long to wait before assuming not ssl */ + int acceptencryptedconnection; /* accept encrypted connections? */ + int acceptunencryptedconnection; /* accept unencrypted connections? */ }; struct iohandler { -- cgit From 273e500c262b84c8cd9a2249ba8955e6f3ff8c74 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sun, 2 Apr 2006 17:32:12 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@59 f02b47b9-160a-0410-81a6-dc3441afb0ec --- astmanproxy.conf | 3 + src/astmanproxy.c | 38 ++++++++++-- src/common.c | 2 +- src/config.c | 7 ++- src/include/astmanproxy.h | 9 +-- src/ssl.c | 2 +- ssl.conf | 154 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 204 insertions(+), 11 deletions(-) create mode 100644 ssl.conf diff --git a/astmanproxy.conf b/astmanproxy.conf index b8ab5d3..95db4e6 100644 --- a/astmanproxy.conf +++ b/astmanproxy.conf @@ -36,6 +36,9 @@ asteriskwritetimeout=100 ; Amount of time to wait before timing out on writes to clients clientwritetimeout=200 +; Our server-side SSL certificate; what we use when answering clients +certfile = /var/lib/asterisk/certs/proxy.pem + ; Address for proxy to listen on, can be set to * or x.x.x.x format ; recommend that you listen only on 127.0.0.1 or on an interface that ; is otherwise locked down to a trusted host, since the proxy diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 0a3a172..29259d3 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -78,7 +78,7 @@ void leave(int sig) { c->output->write(c, &cm); logmsg("Shutdown, closed client %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); } - close(c->fd); + close_sock(c->fd); /* close tcp & ssl socket */ pthread_mutex_destroy(&c->lock); free(c); } @@ -95,7 +95,7 @@ void leave(int sig) { if (debug) debugmsg("Closing listener socket"); - close(asock); + close_sock(asock); /* close tcp & ssl socket */ /* unload io handlers */ while (iohandlers) { @@ -154,7 +154,7 @@ void destroy_session(struct mansession *s) sessions = cur->next; debugmsg("Connection closed: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); if (s->fd > -1) - close(s->fd); + close_sock(s->fd); /* close tcp & ssl socket */ pthread_mutex_destroy(&s->lock); free(s); } else @@ -183,7 +183,7 @@ int WriteClients(struct message *m) { } else c->output->write(c, m); if ( c->input->autodisconnect && c->input->autodisconnect() ) - close(c->fd); + close_sock(c->fd); /* close tcp & ssl socket */ } c = c->next; } @@ -517,6 +517,7 @@ static void *accept_thread() int flags; pthread_attr_t attr; char iabuf[INET_ADDRSTRLEN]; + int is_encrypted; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); @@ -534,6 +535,35 @@ static void *accept_thread() logmsg("Failed to set listener tcp connection to TCP_NODELAY mode: %s\n", strerror(errno)); } } + + /* SSL stuff below */ + is_encrypted = is_encrypt_request(pc.sslclhellotimeout, as); + if (is_encrypted > 0) { + if (!pc.acceptencryptedconnection) { + if( debug ) + debugmsg("Accepting encrypted connection disabled, closing the connection \n"); + close_sock(as); + continue; + } else { + if((as = saccept(as)) >= 0 ) { + if( debug ) + debugmsg("Can't accept the ssl connection, since SSL init has failed for certificate reason\n"); + close_sock(as); + continue; + } + } + } else if (is_encrypted == -1) { + logmsg("SSL version 2 is unsecure, we don't support it\n"); + close_sock(as); + continue; + } + if ( (! pc.acceptunencryptedconnection) && (as >= 0)) { + logmsg("Unencrypted connections are not accepted and we received an unencrypted connection request\n"); + close_sock(as); + continue; + } + /* SSL stuff end */ + s = malloc(sizeof(struct mansession)); if ( !s ) { logmsg("Failed to allocate listener session: %s\n", strerror(errno)); diff --git a/src/common.c b/src/common.c index b52ee59..8ec016d 100644 --- a/src/common.c +++ b/src/common.c @@ -143,7 +143,7 @@ done: fcntl(sockfd, F_SETFL, flags); /* restore file status flags */ if (error) { - /* close(sockfd); */ /* disable for now, we want to retry... */ + /* close(sockfd); */ /* we want to retry... */ errno = error; return(-1); } diff --git a/src/config.c b/src/config.c index 824043b..29213ad 100644 --- a/src/config.c +++ b/src/config.c @@ -105,6 +105,8 @@ void *processline(char *s) { pc.acceptencryptedconnection = strcmp(value,"yes") ? 0 : 1; else if (!strcmp(name,"acceptunencryptedconnection") ) pc.acceptunencryptedconnection = strcmp(value,"yes") ? 0 : 1; + else if (!strcmp(name,"certfile") ) + strcpy(pc.certfile, value); else if (!strcmp(name,"proxykey") ) strcpy(pc.key, value); else if (!strcmp(name,"proc_user") ) @@ -217,7 +219,7 @@ int ReadConfig() { /* Set nonzero config defaults */ pc.asteriskwritetimeout = 100; pc.clientwritetimeout = 100; - pc.sslclhellotimeout = 200; + pc.sslclhellotimeout = 500; sprintf(cfn, "%s/%s", CDIR, CFILE); FP = fopen( cfn, "r" ); @@ -238,6 +240,9 @@ int ReadConfig() { fclose(FP); + /* initialize SSL layer with our server certfile */ + init_secure(pc.certfile); + return 0; } diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index fdd783d..6c4ef0b 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -63,16 +63,17 @@ struct proxyconfig { char outputformat[80]; int autofilter; char key[80]; - char proc_user[30]; - char proc_group[30]; - char logfile[80]; + char proc_user[40]; + char proc_group[40]; + char logfile[256]; int retryinterval; int maxretries; int asteriskwritetimeout; /* ms to wait when writing to asteriskfor ast_carefulwrite */ int clientwritetimeout; /* ms to wait when writing to client ast_carefulwrite */ int sslclhellotimeout; /* ssl client hello timeout -- how long to wait before assuming not ssl */ - int acceptencryptedconnection; /* accept encrypted connections? */ + int acceptencryptedconnection; /* accept encrypted connections? */ int acceptunencryptedconnection; /* accept unencrypted connections? */ + char certfile[256]; /* our SERVER-side SSL certificate file */ }; struct iohandler { diff --git a/src/ssl.c b/src/ssl.c index b884eac..c4377cd 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -215,7 +215,7 @@ int m_recv(int s, void *buf, size_t len, int flags) rec_bytes += ret; if (debug) - debugmsg("Received data from SSL socket - %d\n", ret); + debugmsg("Received data from SSL socket - %d", ret); return ret; } diff --git a/ssl.conf b/ssl.conf new file mode 100644 index 0000000..d10d9a1 --- /dev/null +++ b/ssl.conf @@ -0,0 +1,154 @@ +# Asterisk SSL configuration +# +# OpenSSL configuration file for custom Certificate Authority. Use a +# different openssl.cnf file to generate certificate signing requests; +# this one is for use only in Certificate Authority operations (csr -> +# cert, cert revocation, revocation list generation). +# +# Be sure to customize this file prior to use, e.g. the commonName and +# other options under the root_ca_distinguished_name section. + +HOME = . +RANDFILE = $ENV::HOME/.rnd + +[ ca ] +default_ca = MyAsteriskCA + +[ MyAsteriskCA ] +dir = . +# unsed at present, and my limited certs can be kept in current dir +#certs = $dir/certs +new_certs_dir = $dir/newcerts +crl_dir = $dir/crl +database = $dir/index + +certificate = $dir/ca-cert.pem +serial = $dir/serial +crl = $dir/ca-crl.pem +private_key = $dir/private/ca-key.pem +RANDFILE = $dir/private/.rand + +x509_extensions = usr_cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default +cert_opt = ca_default + +default_crl_days= 30 +default_days = 7300 +# if need to be compatible with older software, use weaker md5 +default_md = sha1 +# MSIE may need following set to yes? +preserve = no + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = US +stateOrProvinceName = CA +organizationName = XYZ +organizationalUnitName = XYZ +commonName = asterisk +emailAddress = root@localhost + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 2048 +default_keyfile = ./private/ca-key.pem +default_md = sha1 + +prompt = no +distinguished_name = root_ca_distinguished_name + +x509_extensions = v3_ca + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString. +# utf8only: only UTF8Strings. +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings +# so use this option with caution! +string_mask = nombstr + +# req_extensions = v3_req + +[ root_ca_distinguished_name ] +commonName = NoSuchCA CA +countryName = US +stateOrProvinceName = California +localityName = San Mateo +0.organizationName = domain.net +emailAddress = nobody@localhost + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer:always + +nsCaRevocationUrl = https://www.sial.org/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + + +# Extensions for a typical CA + +# PKIX recommendation. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always + +# This is what PKIX recommends but some broken software chokes on critical +# extensions. +#basicConstraints = critical,CA:true +# So we do this instead. +basicConstraints = CA:true + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always,issuer:always -- cgit From 1392b8dc7fe62e7c875eb01a70d0b88b030af855 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sun, 2 Apr 2006 17:49:37 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@60 f02b47b9-160a-0410-81a6-dc3441afb0ec --- Makefile | 18 ++++++++++++++++++ astmanproxy.conf | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 37da0d4..10bfe74 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,24 @@ $(OBJS): %.o: %.c $(SOBJS): %.so: %.o $(CC) $(SOLINK) $< -o $@ +SERIAL=0 + +cert: + if [ ! -f /var/lib/asterisk/certs/proxy-server.pem ]; then \ + umask 77 ; \ + PEM1=`/bin/mktemp /tmp/openssl.XXXXXX` ; \ + PEM2=`/bin/mktemp /tmp/openssl.XXXXXX` ; \ + sudo cp ./ssl.conf /etc/asterisk/ssl.conf; \ + /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) -config /etc/asterisk/ssl.conf ; \ + cat $$PEM1 > $@ ; \ + echo "" >> $@ ; \ + cat $$PEM2 >> $@ ; \ + cat $@ > proxy-server.pem ;\ + sudo mkdir -p /var/lib/asterisk/certs; \ + sudo mv $@ /var/lib/asterisk/certs/proxy-server.pem; \ + $(RM) $$PEM1 $$PEM2 $@; \ + fi + install: uninstall all install -d $(BINDIR) install astmanproxy $(BINDIR) diff --git a/astmanproxy.conf b/astmanproxy.conf index 95db4e6..3a017ac 100644 --- a/astmanproxy.conf +++ b/astmanproxy.conf @@ -37,7 +37,7 @@ asteriskwritetimeout=100 clientwritetimeout=200 ; Our server-side SSL certificate; what we use when answering clients -certfile = /var/lib/asterisk/certs/proxy.pem +certfile = /var/lib/asterisk/certs/proxy-server.pem ; Address for proxy to listen on, can be set to * or x.x.x.x format ; recommend that you listen only on 127.0.0.1 or on an interface that -- cgit From 645fd88300ef90cfc4275952dc5cb059a613cd1b Mon Sep 17 00:00:00 2001 From: David Troy Date: Sun, 2 Apr 2006 18:04:44 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@61 f02b47b9-160a-0410-81a6-dc3441afb0ec --- Makefile | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 10bfe74..242da16 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ CFLAGS+= $(DEFINES) -Wall -O2 -D_REENTRANT -fPIC -Isrc/include -I/usr/include/op SRCS := $(MODS:%=%.c) HDRS := astmanproxy.h -all: astmanproxy +all: astmanproxy cert astmanproxy: $(OBJS) $(SOBJS) $(CC) $(CFLAGS) -o $@ $(ASTLINK) $(OBJS) $(LIBS) @@ -83,15 +83,17 @@ cert: umask 77 ; \ PEM1=`/bin/mktemp /tmp/openssl.XXXXXX` ; \ PEM2=`/bin/mktemp /tmp/openssl.XXXXXX` ; \ - sudo cp ./ssl.conf /etc/asterisk/ssl.conf; \ - /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) -config /etc/asterisk/ssl.conf ; \ + if [ ! -f /etc/asterisk/ssl.conf ]; then \ + install ./configs/ssl.conf /etc/asterisk/ssl.conf; \ + fi; \ + /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) -config /etc/asterisk/ssl.conf ; \ cat $$PEM1 > $@ ; \ echo "" >> $@ ; \ cat $$PEM2 >> $@ ; \ cat $@ > proxy-server.pem ;\ - sudo mkdir -p /var/lib/asterisk/certs; \ - sudo mv $@ /var/lib/asterisk/certs/proxy-server.pem; \ - $(RM) $$PEM1 $$PEM2 $@; \ + mkdir -p /var/lib/asterisk/certs; \ + mv $@ /var/lib/asterisk/certs/proxy-server.pem; \ + rm $$PEM1 $$PEM2 $@; \ fi install: uninstall all @@ -121,7 +123,7 @@ dist: clean /usr/bin/ssh www.popvox.com "ln -sf $(DISTDIR)/astmanproxy-${VERSION}-*.tgz $(DISTDIR)/astmanproxy-latest.tgz" clean: - rm -f *.o *.so core *~ astmanproxy; + rm -f *.o *.so core *~ astmanproxy proxy-server.pem; print: more Makefile $(HDRS) $(SRCS) | enscript -Ec -2r -j; exit 0 -- cgit From 07d8870cfd70f5b94d9f8177576442553b434b47 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sun, 2 Apr 2006 18:05:56 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@62 f02b47b9-160a-0410-81a6-dc3441afb0ec --- astmanproxy.conf | 83 ------------------------------------------------ configs/astmanproxy.conf | 83 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 83 deletions(-) delete mode 100644 astmanproxy.conf create mode 100644 configs/astmanproxy.conf diff --git a/astmanproxy.conf b/astmanproxy.conf deleted file mode 100644 index 3a017ac..0000000 --- a/astmanproxy.conf +++ /dev/null @@ -1,83 +0,0 @@ -; astmanproxy.conf -; Asterisk Manager Proxy Configuration Sample -; (C) 2005-2006 David C. Troy - -; List of asterisk host(s) you want to proxy -; host = ip_addr, port, user, secret, events, use_ssl -host = localhost, 5038, dave, moo, on, off -host = 192.168.1.173, 5038, dave, moo, on, on - -;host = 127.0.0.2, 5038, user, secret, on -;host = otherhost, 5038, user, secret, on -;host = newhost, 5030, user, secret, off -;host = oldhost, 5040, user, secret, off -;host = myhost, 5038, user, secret, on - -; Server reconnect interval (in seconds); how often to retry -; Connecting to an asterisk server whose connection was lost -retryinterval = 2 - -; Number of times to retry connecting to a given server -; use 0 for infinitely, or some finite number -maxretries = 10 - -; How long do we wait on the manager port for an SSL session start? (ms) -sslclienthellotimeout = 200 - -; Do we accept encrypted SSL manager connections? -acceptencryptedconnection = yes - -; Do we accept unencrypted manager connections? -acceptunencryptedconnection = no - -; Amount of time to wait before timing out on writes to asterisk -asteriskwritetimeout=100 - -; Amount of time to wait before timing out on writes to clients -clientwritetimeout=200 - -; Our server-side SSL certificate; what we use when answering clients -certfile = /var/lib/asterisk/certs/proxy-server.pem - -; Address for proxy to listen on, can be set to * or x.x.x.x format -; recommend that you listen only on 127.0.0.1 or on an interface that -; is otherwise locked down to a trusted host, since the proxy -; currently provides NO authentication/authorization of its own -listenaddress = * - -; Port for proxy to listen on -listenport = 1234 - -; Setting a proxy key requires proxy client connections to -; specify a ProxyKey: keyvalue header in the first incoming request -; to the proxy. Once this is done the client remains authenticated. -; This allows for a simple security layer. If not specified, -; no key is required, however other security measures (listening on -; a protected interface, behind firewall, iptables, etc) should be -; in place and well understood. -; proxykey = foobar - -; user and group for proxy to run as; will NOT run as root! -proc_user = nobody -proc_group = nobody - -; default input and output format for clients -; inputformat = (standard|xml|http) -; outputformat = (standard|xml|csv) -inputformat = standard -outputformat = standard - -; set autofilter to be on or off by default -; with autofilter on, you can automatically filter responses -; to include only messages related to a specific actionid, -; as specified in a previous message -; valid values: on, off -; can also be changed on a per-client basis using -; ProxyAction: SetAutoFilter -; AutoFilter: (on|off) -autofilter = off - -; location of logfile -- will be owned by proc_user/proc_group -;logfile = /opt/log/asterisk/astmanproxy.log -logfile = /var/log/asterisk/astmanproxy.log - diff --git a/configs/astmanproxy.conf b/configs/astmanproxy.conf new file mode 100644 index 0000000..3a017ac --- /dev/null +++ b/configs/astmanproxy.conf @@ -0,0 +1,83 @@ +; astmanproxy.conf +; Asterisk Manager Proxy Configuration Sample +; (C) 2005-2006 David C. Troy + +; List of asterisk host(s) you want to proxy +; host = ip_addr, port, user, secret, events, use_ssl +host = localhost, 5038, dave, moo, on, off +host = 192.168.1.173, 5038, dave, moo, on, on + +;host = 127.0.0.2, 5038, user, secret, on +;host = otherhost, 5038, user, secret, on +;host = newhost, 5030, user, secret, off +;host = oldhost, 5040, user, secret, off +;host = myhost, 5038, user, secret, on + +; Server reconnect interval (in seconds); how often to retry +; Connecting to an asterisk server whose connection was lost +retryinterval = 2 + +; Number of times to retry connecting to a given server +; use 0 for infinitely, or some finite number +maxretries = 10 + +; How long do we wait on the manager port for an SSL session start? (ms) +sslclienthellotimeout = 200 + +; Do we accept encrypted SSL manager connections? +acceptencryptedconnection = yes + +; Do we accept unencrypted manager connections? +acceptunencryptedconnection = no + +; Amount of time to wait before timing out on writes to asterisk +asteriskwritetimeout=100 + +; Amount of time to wait before timing out on writes to clients +clientwritetimeout=200 + +; Our server-side SSL certificate; what we use when answering clients +certfile = /var/lib/asterisk/certs/proxy-server.pem + +; Address for proxy to listen on, can be set to * or x.x.x.x format +; recommend that you listen only on 127.0.0.1 or on an interface that +; is otherwise locked down to a trusted host, since the proxy +; currently provides NO authentication/authorization of its own +listenaddress = * + +; Port for proxy to listen on +listenport = 1234 + +; Setting a proxy key requires proxy client connections to +; specify a ProxyKey: keyvalue header in the first incoming request +; to the proxy. Once this is done the client remains authenticated. +; This allows for a simple security layer. If not specified, +; no key is required, however other security measures (listening on +; a protected interface, behind firewall, iptables, etc) should be +; in place and well understood. +; proxykey = foobar + +; user and group for proxy to run as; will NOT run as root! +proc_user = nobody +proc_group = nobody + +; default input and output format for clients +; inputformat = (standard|xml|http) +; outputformat = (standard|xml|csv) +inputformat = standard +outputformat = standard + +; set autofilter to be on or off by default +; with autofilter on, you can automatically filter responses +; to include only messages related to a specific actionid, +; as specified in a previous message +; valid values: on, off +; can also be changed on a per-client basis using +; ProxyAction: SetAutoFilter +; AutoFilter: (on|off) +autofilter = off + +; location of logfile -- will be owned by proc_user/proc_group +;logfile = /opt/log/asterisk/astmanproxy.log +logfile = /var/log/asterisk/astmanproxy.log + -- cgit From cb8b0e680884fca4eb7e4f642d59ba98fd46190c Mon Sep 17 00:00:00 2001 From: David Troy Date: Sun, 2 Apr 2006 18:06:22 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@63 f02b47b9-160a-0410-81a6-dc3441afb0ec --- astmanproxy.users | 2 -- configs/astmanproxy.users | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 astmanproxy.users create mode 100644 configs/astmanproxy.users diff --git a/astmanproxy.users b/astmanproxy.users deleted file mode 100644 index 2645fc9..0000000 --- a/astmanproxy.users +++ /dev/null @@ -1,2 +0,0 @@ -; user=secret,channel,out_context (to Asterisk),in_context (From Asterisk) -steve=steve,SIP/snom190,local, diff --git a/configs/astmanproxy.users b/configs/astmanproxy.users new file mode 100644 index 0000000..2645fc9 --- /dev/null +++ b/configs/astmanproxy.users @@ -0,0 +1,2 @@ +; user=secret,channel,out_context (to Asterisk),in_context (From Asterisk) +steve=steve,SIP/snom190,local, -- cgit From f11abe985ed5d6d99fcd218be661d5dbfeacaf9c Mon Sep 17 00:00:00 2001 From: David Troy Date: Sun, 2 Apr 2006 18:06:40 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@64 f02b47b9-160a-0410-81a6-dc3441afb0ec --- configs/ssl.conf | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ ssl.conf | 154 ------------------------------------------------------- 2 files changed, 154 insertions(+), 154 deletions(-) create mode 100644 configs/ssl.conf delete mode 100644 ssl.conf diff --git a/configs/ssl.conf b/configs/ssl.conf new file mode 100644 index 0000000..d10d9a1 --- /dev/null +++ b/configs/ssl.conf @@ -0,0 +1,154 @@ +# Asterisk SSL configuration +# +# OpenSSL configuration file for custom Certificate Authority. Use a +# different openssl.cnf file to generate certificate signing requests; +# this one is for use only in Certificate Authority operations (csr -> +# cert, cert revocation, revocation list generation). +# +# Be sure to customize this file prior to use, e.g. the commonName and +# other options under the root_ca_distinguished_name section. + +HOME = . +RANDFILE = $ENV::HOME/.rnd + +[ ca ] +default_ca = MyAsteriskCA + +[ MyAsteriskCA ] +dir = . +# unsed at present, and my limited certs can be kept in current dir +#certs = $dir/certs +new_certs_dir = $dir/newcerts +crl_dir = $dir/crl +database = $dir/index + +certificate = $dir/ca-cert.pem +serial = $dir/serial +crl = $dir/ca-crl.pem +private_key = $dir/private/ca-key.pem +RANDFILE = $dir/private/.rand + +x509_extensions = usr_cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default +cert_opt = ca_default + +default_crl_days= 30 +default_days = 7300 +# if need to be compatible with older software, use weaker md5 +default_md = sha1 +# MSIE may need following set to yes? +preserve = no + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = US +stateOrProvinceName = CA +organizationName = XYZ +organizationalUnitName = XYZ +commonName = asterisk +emailAddress = root@localhost + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 2048 +default_keyfile = ./private/ca-key.pem +default_md = sha1 + +prompt = no +distinguished_name = root_ca_distinguished_name + +x509_extensions = v3_ca + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString. +# utf8only: only UTF8Strings. +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings +# so use this option with caution! +string_mask = nombstr + +# req_extensions = v3_req + +[ root_ca_distinguished_name ] +commonName = NoSuchCA CA +countryName = US +stateOrProvinceName = California +localityName = San Mateo +0.organizationName = domain.net +emailAddress = nobody@localhost + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer:always + +nsCaRevocationUrl = https://www.sial.org/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + + +# Extensions for a typical CA + +# PKIX recommendation. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always + +# This is what PKIX recommends but some broken software chokes on critical +# extensions. +#basicConstraints = critical,CA:true +# So we do this instead. +basicConstraints = CA:true + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always,issuer:always diff --git a/ssl.conf b/ssl.conf deleted file mode 100644 index d10d9a1..0000000 --- a/ssl.conf +++ /dev/null @@ -1,154 +0,0 @@ -# Asterisk SSL configuration -# -# OpenSSL configuration file for custom Certificate Authority. Use a -# different openssl.cnf file to generate certificate signing requests; -# this one is for use only in Certificate Authority operations (csr -> -# cert, cert revocation, revocation list generation). -# -# Be sure to customize this file prior to use, e.g. the commonName and -# other options under the root_ca_distinguished_name section. - -HOME = . -RANDFILE = $ENV::HOME/.rnd - -[ ca ] -default_ca = MyAsteriskCA - -[ MyAsteriskCA ] -dir = . -# unsed at present, and my limited certs can be kept in current dir -#certs = $dir/certs -new_certs_dir = $dir/newcerts -crl_dir = $dir/crl -database = $dir/index - -certificate = $dir/ca-cert.pem -serial = $dir/serial -crl = $dir/ca-crl.pem -private_key = $dir/private/ca-key.pem -RANDFILE = $dir/private/.rand - -x509_extensions = usr_cert - -# Comment out the following two lines for the "traditional" -# (and highly broken) format. -name_opt = ca_default -cert_opt = ca_default - -default_crl_days= 30 -default_days = 7300 -# if need to be compatible with older software, use weaker md5 -default_md = sha1 -# MSIE may need following set to yes? -preserve = no - -# A few difference way of specifying how similar the request should look -# For type CA, the listed attributes must be the same, and the optional -# and supplied fields are just that :-) -policy = policy_match - -# For the CA policy -[ policy_match ] -countryName = US -stateOrProvinceName = CA -organizationName = XYZ -organizationalUnitName = XYZ -commonName = asterisk -emailAddress = root@localhost - -# For the 'anything' policy -# At this point in time, you must list all acceptable 'object' -# types. -[ policy_anything ] -countryName = optional -stateOrProvinceName = optional -localityName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -#################################################################### -[ req ] -default_bits = 2048 -default_keyfile = ./private/ca-key.pem -default_md = sha1 - -prompt = no -distinguished_name = root_ca_distinguished_name - -x509_extensions = v3_ca - -# Passwords for private keys if not present they will be prompted for -# input_password = secret -# output_password = secret - -# This sets a mask for permitted string types. There are several options. -# default: PrintableString, T61String, BMPString. -# pkix : PrintableString, BMPString. -# utf8only: only UTF8Strings. -# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). -# MASK:XXXX a literal mask value. -# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings -# so use this option with caution! -string_mask = nombstr - -# req_extensions = v3_req - -[ root_ca_distinguished_name ] -commonName = NoSuchCA CA -countryName = US -stateOrProvinceName = California -localityName = San Mateo -0.organizationName = domain.net -emailAddress = nobody@localhost - -[ usr_cert ] - -# These extensions are added when 'ca' signs a request. - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer:always - -nsCaRevocationUrl = https://www.sial.org/ca-crl.pem -#nsBaseUrl -#nsRevocationUrl -#nsRenewalUrl -#nsCaPolicyUrl -#nsSslServerName - -[ v3_req ] - -# Extensions to add to a certificate request - -basicConstraints = CA:FALSE -keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -[ v3_ca ] - - -# Extensions for a typical CA - -# PKIX recommendation. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid:always,issuer:always - -# This is what PKIX recommends but some broken software chokes on critical -# extensions. -#basicConstraints = critical,CA:true -# So we do this instead. -basicConstraints = CA:true - -[ crl_ext ] - -# CRL extensions. -# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. - -# issuerAltName=issuer:copy -authorityKeyIdentifier=keyid:always,issuer:always -- cgit From 891db99d4ea8999915b7fd1077a9f39f8d0ce219 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sun, 2 Apr 2006 18:10:36 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@65 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/ssl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ssl.c b/src/ssl.c index c4377cd..03c9cd8 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -278,7 +278,7 @@ int is_encrypt_request(int sslclhellotimeout, int fd) ready_fdescriptors = select (fd + 1, &listeners, NULL, NULL, &tv); if (ready_fdescriptors < 0 ) { - debugmsg("select returned error, This should not happen: \n"); + debugmsg("is_encrypt_request: select returned error, This should not happen:"); return 0; } else if (ready_fdescriptors == 0) { return 0; @@ -290,12 +290,12 @@ int is_encrypt_request(int sslclhellotimeout, int fd) /* for tls buf[0x02] = 0x01 and ssl v3 buf[0x02] = 0x02 */ ((buf[0x02] == 0x00) || (buf[0x02] == 0x01))) { if (debug) - debugmsg("Received a SSL request\n"); + debugmsg("Received a SSL request"); return 1; /* check for sslv23_client_method */ } else if ((buf[0x02] == 0x01) && (buf[0x03] == 0x03) && (buf[0x04] == 0x01)) { if (debug) - debugmsg("Received a SSL request for SSLv23_client_method()\n"); + debugmsg("Received a SSL request for SSLv23_client_method()"); return 1; } /* check for sslv2 and return -1 */ -- cgit From 7d5a0b34c0f7d35bbdf7ab62672d84c733c00bdd Mon Sep 17 00:00:00 2001 From: David Troy Date: Sun, 2 Apr 2006 18:23:43 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@66 f02b47b9-160a-0410-81a6-dc3441afb0ec --- VERSIONS | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/VERSIONS b/VERSIONS index 4f048ce..9336dd0 100644 --- a/VERSIONS +++ b/VERSIONS @@ -1,17 +1,14 @@ -0.99a Initial Beta Release -0.99b Moved WriteXMLClient into xml.c - Fixed XML tag bug (Bryan Ballard) - Fixed ast_log instances -1.0 First major release -1.0a Added minor Makefile changes - Added Debian package support (Tzafrir Cohen) -1.0b Action: logoff (lowercase) was not intercepted properly (Steven Blatchford) - Fixed SetOutputFormat documentation error (Steven Blatchford) -1.0c Intercept empty command blocks; do not pass to Asterisk - Added call for proxyerror routine, previously unreferenced - Intercept Action: Login; always authenticates and does not pass to * -1.0d Properly intercept SIGPIPE (Brian Jones) +1.20pre Now using ast_carefulwrite for non-blocking writes + Added asteriskwritetimeout config setting for asterisk write timeout + Added clientwritetimeout config setting for client write timeout + Added support for astmanproxy.users user authentication (Steve Davies) + Added support for Action: Challenge/AuthType: MD5 authentication + Added challenge field to mansession structure for use by Action: Challenge + Added writetimeout var to mansession structure for use by ast_carefulwrite + Added SSL Support (Remco Treffkorn, Mahesh Karoshi, John Todd; Tello Corp) + Added 'usessl' option for connecting to asterisk servers +------------------------------------------------------------------------------------------------ 1.1pre2 Completely Modularized and Abstracted Input/Output formats Cleaned up session write mutex code (Peter Loeppky) Fixed message initialization bug -- using wrong sizeof @@ -42,12 +39,17 @@ Added support for Mac OS X (Tested on 10.3.9); BSD may also work Aborts on old config file format (detects incomplete server spec) -1.20pre Now using ast_carefulwrite for non-blocking writes - Added asteriskwritetimeout config setting for asterisk write timeout - Added clientwritetimeout config setting for client write timeout - Added 'usessl' option for connecting to asterisk servers - Added support for astmanproxy.users user authentication (Steve Davies) - Added support for Action: Challenge/AuthType: MD5 authentication - Added challenge field to mansession structure for use by Action: Challenge - Added writetimeout var to mansession structure for use by ast_carefulwrite - +------------------------------------------------------------------------------------------------ +0.99a Initial Beta Release +0.99b Moved WriteXMLClient into xml.c + Fixed XML tag bug (Bryan Ballard) + Fixed ast_log instances +1.0 First major release +1.0a Added minor Makefile changes + Added Debian package support (Tzafrir Cohen) +1.0b Action: logoff (lowercase) was not intercepted properly (Steven Blatchford) + Fixed SetOutputFormat documentation error (Steven Blatchford) +1.0c Intercept empty command blocks; do not pass to Asterisk + Added call for proxyerror routine, previously unreferenced + Intercept Action: Login; always authenticates and does not pass to * +1.0d Properly intercept SIGPIPE (Brian Jones) -- cgit From 7885ebaa4f79cf908adb9e2a7fb252493c10026b Mon Sep 17 00:00:00 2001 From: David Troy Date: Sun, 2 Apr 2006 19:04:27 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@67 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/common.c | 12 ++++++++---- src/http.c | 15 ++++++++++----- src/ssl.c | 4 ++-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/common.c b/src/common.c index 8ec016d..0855ac9 100644 --- a/src/common.c +++ b/src/common.c @@ -11,7 +11,8 @@ int get_input(struct mansession *s, char *output) struct pollfd fds[1]; char iabuf[INET_ADDRSTRLEN]; - debugmsg("in get_input"); + if (debug > 3) + debugmsg("in get_input"); /* Look for \r\n from the front, our preferred end of line */ for (x=0;xinlen;x++) { @@ -36,19 +37,22 @@ int get_input(struct mansession *s, char *output) debugmsg("Warning: Got long line with no end from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf); s->inlen = 0; } - debugmsg("attempting poll operation"); + if (debug > 3) + debugmsg("attempting poll operation"); /* get actual fd, even if a negative SSL fd */ fds[0].fd = get_real_fd(s->fd); fds[0].events = POLLIN; res = poll(fds, 1, -1); - debugmsg("returned from poll op"); + if (debug > 3) + debugmsg("returned from poll op"); + if (res < 0 && debug) { debugmsg("Select returned error"); } else if (res > 0) { - pthread_mutex_lock(&s->lock); debugmsg("attempting socket read in get_input..."); + pthread_mutex_lock(&s->lock); /* read from socket; SSL or otherwise */ res = m_recv(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen, 0); pthread_mutex_unlock(&s->lock); diff --git a/src/http.c b/src/http.c index b91b872..d8b081d 100644 --- a/src/http.c +++ b/src/http.c @@ -16,6 +16,8 @@ int ParseHTTPInput(char *buf, struct message *m) { if ( !(*buf) ) return 0; */ + debugmsg("Parsing http input: %s", buf); + /* initialize message block */ memset(m, 0, sizeof (struct message) ); @@ -93,11 +95,13 @@ int _read(struct mansession *s, struct message *m) { s->inlen -= clength; pthread_mutex_unlock(&s->lock); } - debugmsg("method: %s", method); - debugmsg("clength: %d", clength); - debugmsg("formdata: %s", formdata); - debugmsg("s->inbuf: %s", s->inbuf); - debugmsg("s->inlen: %d", s->inlen); + if (debug) { + debugmsg("method: %s", method); + debugmsg("clength: %d", clength); + debugmsg("formdata: %s", formdata); + debugmsg("s->inbuf: %s", s->inbuf); + debugmsg("s->inlen: %d", s->inlen); + } BuildHTTPHeader(header); pthread_mutex_lock(&s->lock); @@ -121,6 +125,7 @@ int _read(struct mansession *s, struct message *m) { memcpy(formdata, line+6, strstr(line, " HTTP")-line-6); } else if ( !strncasecmp(line, "Content-Length: ", 16) ) { clength = atoi(line+16); + debugmsg("clength=%d", clength); } } } else if (res < 0) diff --git a/src/ssl.c b/src/ssl.c index 03c9cd8..7f57362 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -214,8 +214,8 @@ int m_recv(int s, void *buf, size_t len, int flags) if (ret > 0) rec_bytes += ret; - if (debug) - debugmsg("Received data from SSL socket - %d", ret); + if (debug && s<-1) + debugmsg("Received %d bytes from SSL socket", ret); return ret; } -- cgit From 09255fbd6f7e0bd81f5de30f580928a94e1834d7 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sun, 2 Apr 2006 21:24:16 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@68 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/astmanproxy.c | 6 +++--- src/common.c | 3 ++- src/http.c | 49 +++++++++++++++++++------------------------------ 3 files changed, 24 insertions(+), 34 deletions(-) diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 29259d3..282a33e 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -153,8 +153,8 @@ void destroy_session(struct mansession *s) else sessions = cur->next; debugmsg("Connection closed: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); - if (s->fd > -1) - close_sock(s->fd); /* close tcp & ssl socket */ + + close_sock(s->fd); /* close tcp/ssl socket */ pthread_mutex_destroy(&s->lock); free(s); } else @@ -183,7 +183,7 @@ int WriteClients(struct message *m) { } else c->output->write(c, m); if ( c->input->autodisconnect && c->input->autodisconnect() ) - close_sock(c->fd); /* close tcp & ssl socket */ + destroy_session(c); } c = c->next; } diff --git a/src/common.c b/src/common.c index 0855ac9..39b5c93 100644 --- a/src/common.c +++ b/src/common.c @@ -59,10 +59,11 @@ int get_input(struct mansession *s, char *output) if (res < 1) return -1; } + + /* We have some input, but it's not ready for processing */ s->inlen += res; s->inbuf[s->inlen] = '\0'; return 0; - /* We have some input, but it's not ready for processing */ } char *astman_get_header(struct message *m, char *var) diff --git a/src/http.c b/src/http.c index d8b081d..f14c722 100644 --- a/src/http.c +++ b/src/http.c @@ -16,8 +16,6 @@ int ParseHTTPInput(char *buf, struct message *m) { if ( !(*buf) ) return 0; */ - debugmsg("Parsing http input: %s", buf); - /* initialize message block */ memset(m, 0, sizeof (struct message) ); @@ -82,11 +80,19 @@ int _read(struct mansession *s, struct message *m) { memset(line, 0, sizeof line); res = get_input(s, line); - if (res > 0) { - if (*line == '\0' ) { - if (*method == '\0') - break; - else { + if (res > 0 && *line) { + debugmsg("Got http: %s", line); + /* Do meaningful things here */ + if ( !strncmp(line,"POST",4) ) { + strncpy(method, line, 4); + } else if ( !strncmp(line,"GET",3) ) { + /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */ + strncpy(method, line, 3); + memcpy(formdata, line+6, strstr(line, " HTTP")-line-6); + } else if ( !strncasecmp(line, "Content-Length: ", 16) ) { + clength = atoi(line+16); + } + } else if (res == 0 && *method && clength && *s->inbuf && strlen(s->inbuf)==clength) { if ( !strcasecmp(method, "POST") ) { pthread_mutex_lock(&s->lock); strncpy(formdata, s->inbuf, clength); @@ -95,13 +101,11 @@ int _read(struct mansession *s, struct message *m) { s->inlen -= clength; pthread_mutex_unlock(&s->lock); } - if (debug) { - debugmsg("method: %s", method); - debugmsg("clength: %d", clength); - debugmsg("formdata: %s", formdata); - debugmsg("s->inbuf: %s", s->inbuf); - debugmsg("s->inlen: %d", s->inlen); - } + debugmsg("method: %s", method); + debugmsg("clength: %d", clength); + debugmsg("formdata: %s", formdata); + debugmsg("s->inbuf: %s", s->inbuf); + debugmsg("s->inlen: %d", s->inlen); BuildHTTPHeader(header); pthread_mutex_lock(&s->lock); @@ -113,23 +117,8 @@ int _read(struct mansession *s, struct message *m) { /* now, let's transform and copy into a standard message block */ res = ParseHTTPInput(formdata, m); return res; - } - } else { - debugmsg("Got http: %s", line); - /* Do meaningful things here */ - if ( !strncmp(line,"POST",4) ) { - strncpy(method, line, 4); - } else if ( !strncmp(line,"GET",3) ) { - /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */ - strncpy(method, line, 3); - memcpy(formdata, line+6, strstr(line, " HTTP")-line-6); - } else if ( !strncasecmp(line, "Content-Length: ", 16) ) { - clength = atoi(line+16); - debugmsg("clength=%d", clength); - } - } } else if (res < 0) - return res; + break; } return -1; -- cgit From 86d13969f343e282a421acaa00796f9ffb95faed Mon Sep 17 00:00:00 2001 From: David Troy Date: Mon, 3 Apr 2006 01:15:22 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@69 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/astmanproxy.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 282a33e..3c976e6 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -153,7 +153,6 @@ void destroy_session(struct mansession *s) else sessions = cur->next; debugmsg("Connection closed: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); - close_sock(s->fd); /* close tcp/ssl socket */ pthread_mutex_destroy(&s->lock); free(s); @@ -169,7 +168,7 @@ void destroy_session(struct mansession *s) } int WriteClients(struct message *m) { - struct mansession *c; + struct mansession *c, *d; char *actionid; c = sessions; @@ -182,8 +181,12 @@ int WriteClients(struct message *m) { } } else c->output->write(c, m); - if ( c->input->autodisconnect && c->input->autodisconnect() ) - destroy_session(c); + if ( c->input->autodisconnect && c->input->autodisconnect() ) { + d = c; /* session to disconnect */ + c = c->next; + destroy_session(d); + continue; + } } c = c->next; } -- cgit From dc56649b0e188704512719be80ecb6f544cef0e7 Mon Sep 17 00:00:00 2001 From: David Troy Date: Mon, 3 Apr 2006 01:57:46 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@70 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/astmanproxy.c | 2 +- src/http.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 3c976e6..cc86859 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -576,7 +576,7 @@ static void *accept_thread() memcpy(&s->sin, &sin, sizeof(sin)); /* For safety, make sure socket is non-blocking */ - flags = fcntl(as, F_GETFL); + flags = fcntl(get_real_fd(as), F_GETFL); fcntl(get_real_fd(as), F_SETFL, flags | O_NONBLOCK); pthread_mutex_init(&s->lock, NULL); diff --git a/src/http.c b/src/http.c index f14c722..70cc2b9 100644 --- a/src/http.c +++ b/src/http.c @@ -74,6 +74,7 @@ int _read(struct mansession *s, struct message *m) { memset(method, 0, sizeof method); memset(formdata, 0, sizeof formdata); + memset(header, 0, sizeof header); /* for http, don't do get_input forever */ for (;;) { -- cgit From bbf337d7f093d32703477f2722591ac08f8230bb Mon Sep 17 00:00:00 2001 From: David Troy Date: Mon, 3 Apr 2006 16:23:01 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@71 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/astmanproxy.c | 3 ++- src/common.c | 39 +++++++++++++++++++++++---------------- src/http.c | 18 ++++++++---------- src/include/astmanproxy.h | 2 ++ src/ssl.c | 2 ++ 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/astmanproxy.c b/src/astmanproxy.c index cc86859..853b8bf 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -541,6 +541,7 @@ static void *accept_thread() /* SSL stuff below */ is_encrypted = is_encrypt_request(pc.sslclhellotimeout, as); + debugmsg("is_encrypted: %d", is_encrypted); if (is_encrypted > 0) { if (!pc.acceptencryptedconnection) { if( debug ) @@ -619,7 +620,7 @@ int main(int argc, char *argv[]) { switch( i ) { case 'd': - debug = 1; + debug++; break; case 'h': Usage(); diff --git a/src/common.c b/src/common.c index 39b5c93..98f33e0 100644 --- a/src/common.c +++ b/src/common.c @@ -43,22 +43,29 @@ int get_input(struct mansession *s, char *output) fds[0].fd = get_real_fd(s->fd); fds[0].events = POLLIN; - res = poll(fds, 1, -1); - if (debug > 3) - debugmsg("returned from poll op"); - - if (res < 0 && debug) { - debugmsg("Select returned error"); - } else if (res > 0) { - debugmsg("attempting socket read in get_input..."); - - pthread_mutex_lock(&s->lock); - /* read from socket; SSL or otherwise */ - res = m_recv(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen, 0); - pthread_mutex_unlock(&s->lock); - if (res < 1) - return -1; - } + do { + res = poll(fds, 1, -1); + if (res < 0) { + if (errno == EINTR) { + if (s->dead) + return -1; + continue; + } + if (debug) + debugmsg("Select returned error"); + return -1; + } else if (res > 0) { + debugmsg("attempting socket read in get_input..."); + pthread_mutex_lock(&s->lock); + /* read from socket; SSL or otherwise */ + res = m_recv(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen, 0); + pthread_mutex_unlock(&s->lock); + if (res < 1) + return -1; + break; + + } + } while(1); /* We have some input, but it's not ready for processing */ s->inlen += res; diff --git a/src/http.c b/src/http.c index 70cc2b9..3b271b9 100644 --- a/src/http.c +++ b/src/http.c @@ -12,12 +12,9 @@ int ParseHTTPInput(char *buf, struct message *m) { char *n, *v; - /* just an empty block; go home - if ( !(*buf) ) - return 0; */ - - /* initialize message block */ + /* initialize message block memset(m, 0, sizeof (struct message) ); + */ n = buf; while ( (v = strstr(n, "=")) ) { @@ -80,8 +77,9 @@ int _read(struct mansession *s, struct message *m) { for (;;) { memset(line, 0, sizeof line); res = get_input(s, line); + debugmsg("res=%d, line: %s",res, line); - if (res > 0 && *line) { + if (res > 0) { debugmsg("Got http: %s", line); /* Do meaningful things here */ if ( !strncmp(line,"POST",4) ) { @@ -93,13 +91,12 @@ int _read(struct mansession *s, struct message *m) { } else if ( !strncasecmp(line, "Content-Length: ", 16) ) { clength = atoi(line+16); } - } else if (res == 0 && *method && clength && *s->inbuf && strlen(s->inbuf)==clength) { + + if (*method && s->inlen==clength) { if ( !strcasecmp(method, "POST") ) { pthread_mutex_lock(&s->lock); strncpy(formdata, s->inbuf, clength); - /* Move remaining data back to the front */ - memmove(s->inbuf, s->inbuf + clength+1, s->inlen - clength); - s->inlen -= clength; + s->inlen = 0; pthread_mutex_unlock(&s->lock); } debugmsg("method: %s", method); @@ -118,6 +115,7 @@ int _read(struct mansession *s, struct message *m) { /* now, let's transform and copy into a standard message block */ res = ParseHTTPInput(formdata, m); return res; + } } else if (res < 0) break; } diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index 6c4ef0b..11ca106 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -99,6 +99,8 @@ struct mansession { int inputcomplete; int authenticated; int connected; + int dead; /* Whether we are dead */ + int busy; /* Whether we are busy */ struct ast_server *server; struct proxy_user user; char actionid[MAX_LEN]; diff --git a/src/ssl.c b/src/ssl.c index 7f57362..265c961 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -300,6 +300,8 @@ int is_encrypt_request(int sslclhellotimeout, int fd) } /* check for sslv2 and return -1 */ else if ((buf[0x02] == 0x01) && (buf[0x03] == 0x00) && (buf[0x04] == 0x02)) { + if (debug) + debugmsg("Received a SSLv2 request()"); return -1; } } -- cgit From f1ae22daac2c0e4fe9777942f50d5a3af0470744 Mon Sep 17 00:00:00 2001 From: David Troy Date: Mon, 3 Apr 2006 17:12:52 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@72 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/include/astmanproxy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index 11ca106..4068299 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -25,9 +25,9 @@ #include #endif -#define BUFSIZE 150 +#define BUFSIZE 1024 #define MAX_HEADERS 256 -#define MAX_LEN 150 +#define MAX_LEN 1024 #define PROXY_BANNER "Asterisk Call Manager Proxy" #define PROXY_SHUTDOWN "ProxyMessage: Proxy Shutting Down" -- cgit From ecc49089c697a2cb7331358def5ce7640fae65bf Mon Sep 17 00:00:00 2001 From: David Troy Date: Mon, 3 Apr 2006 18:14:45 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@73 f02b47b9-160a-0410-81a6-dc3441afb0ec --- Makefile | 3 +-- src/astmanproxy.c | 11 +++-------- src/http.c | 5 ++--- src/include/astmanproxy.h | 1 - 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 242da16..4f7e50c 100644 --- a/Makefile +++ b/Makefile @@ -58,8 +58,7 @@ DEFINES+='-DMDIR="$(MODDIR)"' '-DPDIR="$(PERMDIR)"' '-DPFILE="$(PERMFILE)"' VPATH = src # Add -g below for debug/GDB symbols -#CFLAGS+= $(DEFINES) -Wall -O2 -dynamic -D_REENTRANT -fPIC -CFLAGS+= $(DEFINES) -Wall -O2 -D_REENTRANT -fPIC -Isrc/include -I/usr/include/openssl -I- +CFLAGS+= $(DEFINES) -g -Wall -O2 -D_REENTRANT -fPIC -Isrc/include -I/usr/include/openssl -I- # For printing only SRCS := $(MODS:%=%.c) diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 853b8bf..457a3bf 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -181,12 +181,8 @@ int WriteClients(struct message *m) { } } else c->output->write(c, m); - if ( c->input->autodisconnect && c->input->autodisconnect() ) { - d = c; /* session to disconnect */ - c = c->next; - destroy_session(d); - continue; - } + if ( c->input->autodisconnect && c->input->autodisconnect() ) + c->dead = 1; } c = c->next; } @@ -298,7 +294,7 @@ void *session_do(struct mansession *s) destroy_session(s); if (debug) - debugmsg("Exiting session_do thread"); + debugmsg("--- exiting session_do thread ---"); pthread_exit(NULL); return NULL; } @@ -584,7 +580,6 @@ static void *accept_thread() s->fd = as; SetIOHandlers(s, pc.inputformat, pc.outputformat); s->autofilter = pc.autofilter; - s->inputcomplete = 0; s->server = NULL; pthread_mutex_lock(&sessionlock); diff --git a/src/http.c b/src/http.c index 3b271b9..91c3de0 100644 --- a/src/http.c +++ b/src/http.c @@ -66,8 +66,8 @@ int _read(struct mansession *s, struct message *m) { char line[MAX_LEN], method[10], formdata[MAX_LEN], header[MAX_LEN]; int res, clength = 0; - if (s->inputcomplete) - return 0; + if (s->dead) + return -1; memset(method, 0, sizeof method); memset(formdata, 0, sizeof formdata); @@ -107,7 +107,6 @@ int _read(struct mansession *s, struct message *m) { BuildHTTPHeader(header); pthread_mutex_lock(&s->lock); - s->inputcomplete = 1; ast_carefulwrite(s->fd, header, strlen(header), s->writetimeout); pthread_mutex_unlock(&s->lock); debugmsg("header: %s", header); diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index 4068299..fc80412 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -96,7 +96,6 @@ struct mansession { struct iohandler *input; struct iohandler *output; int autofilter; - int inputcomplete; int authenticated; int connected; int dead; /* Whether we are dead */ -- cgit From 7122b49be0d135619e6eb848798b4bc620a9b808 Mon Sep 17 00:00:00 2001 From: David Troy Date: Mon, 3 Apr 2006 19:39:36 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@74 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/astmanproxy.c | 11 +++++++++-- src/common.c | 6 ------ src/http.c | 14 +++++++------- src/include/astmanproxy.h | 2 ++ src/standard.c | 2 -- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 457a3bf..25aab23 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -181,8 +181,11 @@ int WriteClients(struct message *m) { } } else c->output->write(c, m); - if ( c->input->autodisconnect && c->input->autodisconnect() ) - c->dead = 1; + if (c->inputcomplete) { + pthread_mutex_lock(&c->lock); + c->outputcomplete = 1; + pthread_mutex_unlock(&c->lock); + } } c = c->next; } @@ -254,7 +257,11 @@ void *session_do(struct mansession *s) for (;;) { /* Get a complete message block from input handler */ memset(&m, 0, sizeof(struct message) ); + if (debug > 3) + debugmsg("calling %s_read...", s->input->formatname); res = s->input->read(s, &m); + if (debug > 3) + debugmsg("%s_read result =%d", s->input->formatname, res); m.session = s; if (res > 0) { diff --git a/src/common.c b/src/common.c index 98f33e0..f4b370e 100644 --- a/src/common.c +++ b/src/common.c @@ -11,9 +11,6 @@ int get_input(struct mansession *s, char *output) struct pollfd fds[1]; char iabuf[INET_ADDRSTRLEN]; - if (debug > 3) - debugmsg("in get_input"); - /* Look for \r\n from the front, our preferred end of line */ for (x=0;xinlen;x++) { int xtra = 0; @@ -37,8 +34,6 @@ int get_input(struct mansession *s, char *output) debugmsg("Warning: Got long line with no end from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf); s->inlen = 0; } - if (debug > 3) - debugmsg("attempting poll operation"); /* get actual fd, even if a negative SSL fd */ fds[0].fd = get_real_fd(s->fd); @@ -55,7 +50,6 @@ int get_input(struct mansession *s, char *output) debugmsg("Select returned error"); return -1; } else if (res > 0) { - debugmsg("attempting socket read in get_input..."); pthread_mutex_lock(&s->lock); /* read from socket; SSL or otherwise */ res = m_recv(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen, 0); diff --git a/src/http.c b/src/http.c index 91c3de0..fcc6bcc 100644 --- a/src/http.c +++ b/src/http.c @@ -12,10 +12,6 @@ int ParseHTTPInput(char *buf, struct message *m) { char *n, *v; - /* initialize message block - memset(m, 0, sizeof (struct message) ); - */ - n = buf; while ( (v = strstr(n, "=")) ) { v += 1; @@ -66,15 +62,18 @@ int _read(struct mansession *s, struct message *m) { char line[MAX_LEN], method[10], formdata[MAX_LEN], header[MAX_LEN]; int res, clength = 0; - if (s->dead) - return -1; - memset(method, 0, sizeof method); memset(formdata, 0, sizeof formdata); memset(header, 0, sizeof header); /* for http, don't do get_input forever */ for (;;) { + + if (s->inputcomplete && !s->outputcomplete) + continue; + else if (s->inputcomplete && s->outputcomplete) + return -1; + memset(line, 0, sizeof line); res = get_input(s, line); debugmsg("res=%d, line: %s",res, line); @@ -107,6 +106,7 @@ int _read(struct mansession *s, struct message *m) { BuildHTTPHeader(header); pthread_mutex_lock(&s->lock); + s->inputcomplete = 1; ast_carefulwrite(s->fd, header, strlen(header), s->writetimeout); pthread_mutex_unlock(&s->lock); debugmsg("header: %s", header); diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index fc80412..f888191 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -100,6 +100,8 @@ struct mansession { int connected; int dead; /* Whether we are dead */ int busy; /* Whether we are busy */ + int inputcomplete; /* Whether we want any more input from this session (http) */ + int outputcomplete; /* Whether output to this session is done (http) */ struct ast_server *server; struct proxy_user user; char actionid[MAX_LEN]; diff --git a/src/standard.c b/src/standard.c index 26cd4a8..0b2bd17 100644 --- a/src/standard.c +++ b/src/standard.c @@ -15,7 +15,6 @@ extern struct mansession *sessions; int _read(struct mansession *s, struct message *m) { int res; - if (debug) debugmsg("in standard_read module..."); for (;;) { res = get_input(s, m->headers[m->hdrcount]); @@ -46,7 +45,6 @@ int _read(struct mansession *s, struct message *m) { int _write(struct mansession *s, struct message *m) { int i; - if (debug) debugmsg("in standard_write module..."); pthread_mutex_lock(&s->lock); for (i=0; ihdrcount; i++) { ast_carefulwrite(s->fd, m->headers[i], strlen(m->headers[i]) , s->writetimeout); -- cgit From 45f93b7efc02bc2d6d2c3a5150ee7065ee7ae482 Mon Sep 17 00:00:00 2001 From: David Troy Date: Mon, 3 Apr 2006 19:56:20 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@75 f02b47b9-160a-0410-81a6-dc3441afb0ec --- TODO | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index decd317..224e4b8 100644 --- a/TODO +++ b/TODO @@ -7,5 +7,5 @@ tcpwrappers/access control/connect mask? libtool/autoconf/automake support SetInputFormat proxyaction? -kill http clients better? -snmp? +clean up autodisconnect vs. inputcomplete/outputcomplete +deal with no empty http better (no formdata) -- cgit From 6a521bdca3afa676e110ba2bada7698f41504084 Mon Sep 17 00:00:00 2001 From: David Troy Date: Mon, 3 Apr 2006 20:56:45 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@76 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/http.c | 64 ++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/http.c b/src/http.c index fcc6bcc..ff876a1 100644 --- a/src/http.c +++ b/src/http.c @@ -80,45 +80,47 @@ int _read(struct mansession *s, struct message *m) { if (res > 0) { debugmsg("Got http: %s", line); - /* Do meaningful things here */ - if ( !strncmp(line,"POST",4) ) { - strncpy(method, line, 4); - } else if ( !strncmp(line,"GET",3) ) { - /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */ - strncpy(method, line, 3); - memcpy(formdata, line+6, strstr(line, " HTTP")-line-6); - } else if ( !strncasecmp(line, "Content-Length: ", 16) ) { - clength = atoi(line+16); - } - - if (*method && s->inlen==clength) { - if ( !strcasecmp(method, "POST") ) { + + if ( !clength && !strncasecmp(line, "Content-Length: ", 16) ) + clength = atoi(line+16); + + if (!*method) { + if ( !strncmp(line,"POST",4) ) { + strncpy(method, line, 4); + } else if ( !strncmp(line,"GET",3) && strlen(line)>14 ) { + /* GET / HTTP/1.1 ---- this is bad */ + /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */ + strncpy(method, line, 3); + memcpy(formdata, line+6, strstr(line, " HTTP")-line-6); + } + } + } else if (res == 0) { + if (*method && !*formdata) { + if ( !strcasecmp(method, "POST") && clength && s->inlen==clength) { pthread_mutex_lock(&s->lock); strncpy(formdata, s->inbuf, clength); s->inlen = 0; pthread_mutex_unlock(&s->lock); } - debugmsg("method: %s", method); - debugmsg("clength: %d", clength); - debugmsg("formdata: %s", formdata); - debugmsg("s->inbuf: %s", s->inbuf); - debugmsg("s->inlen: %d", s->inlen); - - BuildHTTPHeader(header); - pthread_mutex_lock(&s->lock); - s->inputcomplete = 1; - ast_carefulwrite(s->fd, header, strlen(header), s->writetimeout); - pthread_mutex_unlock(&s->lock); - debugmsg("header: %s", header); - - /* now, let's transform and copy into a standard message block */ - res = ParseHTTPInput(formdata, m); - return res; } - } else if (res < 0) + } + + if (res < 0) break; - } + if (*method && *formdata) { + BuildHTTPHeader(header); + pthread_mutex_lock(&s->lock); + s->inputcomplete = 1; + ast_carefulwrite(s->fd, header, strlen(header), s->writetimeout); + pthread_mutex_unlock(&s->lock); + debugmsg("header: %s", header); + + /* now, let's transform and copy into a standard message block */ + res = ParseHTTPInput(formdata, m); + return res; + } + } return -1; } -- cgit From 4af218734ff2ac7da1a6bdd55fde1ea71848fee7 Mon Sep 17 00:00:00 2001 From: David Troy Date: Mon, 3 Apr 2006 21:33:51 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@77 f02b47b9-160a-0410-81a6-dc3441afb0ec --- TODO | 2 +- src/http.c | 74 ++++++++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/TODO b/TODO index 224e4b8..5febf5a 100644 --- a/TODO +++ b/TODO @@ -8,4 +8,4 @@ libtool/autoconf/automake support SetInputFormat proxyaction? clean up autodisconnect vs. inputcomplete/outputcomplete -deal with no empty http better (no formdata) +deal with no empty http better (no formdata; return error header?) diff --git a/src/http.c b/src/http.c index ff876a1..8a8d245 100644 --- a/src/http.c +++ b/src/http.c @@ -32,24 +32,43 @@ int ParseHTTPInput(char *buf, struct message *m) { return (m->hdrcount > 0); } -int BuildHTTPHeader(char *hdr) { +int HTTPHeader(struct mansession *s, char *status) { time_t t; struct tm tm; char date[80]; + char ctype[15], hdr[MAX_LEN]; time(&t); localtime_r(&t, &tm); strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm); - sprintf(hdr, - "HTTP/1.1 200 OK\r\n" - "Date: %s\r\n" - "Content-Type: text/xml\r\n" - "Connection: close\r\n" - "Server: %s/%s\r\n\r\n", - date, PROXY_BANNER, PROXY_VERSION); + if ( !strcasecmp("xml", s->output->formatname) ) + sprintf(ctype, "text/xml"); + else + sprintf(ctype, "text/plain"); + + if (!strcmp("200 OK", status) ) + sprintf(hdr, + "HTTP/1.1 %s\r\n" + "Date: %s\r\n" + "Content-Type: %s\r\n" + "Connection: close\r\n" + "Server: %s/%s\r\n\r\n", status, + date, ctype, PROXY_BANNER, PROXY_VERSION); + else + sprintf(hdr, + "HTTP/1.1 %s\r\n" + "Date: %s\r\n" + "Status: %s\r\n" + "Server: %s/%s\r\n\r\n", status, date, status, PROXY_BANNER, PROXY_VERSION); + + pthread_mutex_lock(&s->lock); + s->inputcomplete = 1; + ast_carefulwrite(s->fd, hdr, strlen(hdr), s->writetimeout); + pthread_mutex_unlock(&s->lock); + debugmsg("http header: %s", hdr); return 0; } @@ -59,12 +78,12 @@ int _read(struct mansession *s, struct message *m) { /* Note: No single line may be longer than MAX_LEN/s->inbuf, as per get_input */ /* No HTTP Input may be longer than BUFSIZE */ - char line[MAX_LEN], method[10], formdata[MAX_LEN], header[MAX_LEN]; + char line[MAX_LEN], method[10], formdata[MAX_LEN], status[15]; int res, clength = 0; memset(method, 0, sizeof method); memset(formdata, 0, sizeof formdata); - memset(header, 0, sizeof header); + memset(status, 0, sizeof status); /* for http, don't do get_input forever */ for (;;) { @@ -87,11 +106,15 @@ int _read(struct mansession *s, struct message *m) { if (!*method) { if ( !strncmp(line,"POST",4) ) { strncpy(method, line, 4); - } else if ( !strncmp(line,"GET",3) && strlen(line)>14 ) { - /* GET / HTTP/1.1 ---- this is bad */ - /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */ - strncpy(method, line, 3); - memcpy(formdata, line+6, strstr(line, " HTTP")-line-6); + } else if ( !strncmp(line,"GET",3)) { + if ( strlen(line) > 14 ) { + /* GET / HTTP/1.1 ---- this is bad */ + /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */ + strncpy(method, line, 3); + memcpy(formdata, line+6, strstr(line, " HTTP")-line-6); + sprintf(status, "200 OK"); + } else + sprintf(status, "501 Not Implemented"); } } } else if (res == 0) { @@ -101,6 +124,7 @@ int _read(struct mansession *s, struct message *m) { strncpy(formdata, s->inbuf, clength); s->inlen = 0; pthread_mutex_unlock(&s->lock); + sprintf(status, "200 OK"); } } } @@ -108,17 +132,19 @@ int _read(struct mansession *s, struct message *m) { if (res < 0) break; - if (*method && *formdata) { - BuildHTTPHeader(header); - pthread_mutex_lock(&s->lock); - s->inputcomplete = 1; - ast_carefulwrite(s->fd, header, strlen(header), s->writetimeout); - pthread_mutex_unlock(&s->lock); - debugmsg("header: %s", header); + if (*status) { + HTTPHeader(s, status); /* now, let's transform and copy into a standard message block */ - res = ParseHTTPInput(formdata, m); - return res; + if (!strcmp("200 OK", status) ) { + res = ParseHTTPInput(formdata, m); + return res; + } else { + pthread_mutex_lock(&s->lock); + s->outputcomplete = 1; + pthread_mutex_unlock(&s->lock); + return 0; + } } } return -1; -- cgit From b93c5589c08ed90bb318557a32f7cae649727c1d Mon Sep 17 00:00:00 2001 From: David Troy Date: Tue, 4 Apr 2006 16:22:54 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@78 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/config.c | 5 ++++- src/include/astmanproxy.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/config.c b/src/config.c index 29213ad..02d76ec 100644 --- a/src/config.c +++ b/src/config.c @@ -41,10 +41,13 @@ void *add_server(char *srvspec) { case 4: strncat(srv->ast_events, s, 1); break; + case 5: + srv->use_ssl = strcmp(s,"yes") ? 0 : 1; + break; } } while (*(s++)); - if (!srv->ast_host || !srv->ast_port || !srv->ast_user || !srv->ast_pass || !srv->ast_events) { + if (!srv->ast_host || !srv->ast_port || !srv->ast_user || !srv->ast_pass || !srv->ast_events || !srv->use_ssl) { fprintf(stderr, "Aborting! Server spec incomplete: %s!\n", srvspec); free(srv); exit(1); diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index f888191..1d7ec47 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -40,7 +40,7 @@ struct ast_server { char ast_user[80]; char ast_pass[80]; char ast_events[10]; - char use_ssl[5]; + int use_ssl; /* Use SSL when Connecting to Server? */ int status; /* TODO: have this mean something */ struct ast_server *next; }; -- cgit From e4b66ca5172292a09ac0f68b9373fcbe83cef8d6 Mon Sep 17 00:00:00 2001 From: David Troy Date: Tue, 4 Apr 2006 19:39:39 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@79 f02b47b9-160a-0410-81a6-dc3441afb0ec --- Makefile | 2 +- src/astmanproxy.c | 15 ++++----- src/common.c | 54 ------------------------------- src/config.c | 16 ++++++---- src/include/astmanproxy.h | 1 - src/ssl.c | 81 +++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 96 insertions(+), 73 deletions(-) diff --git a/Makefile b/Makefile index 4f7e50c..b9b4f92 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ OBJS += $(MODS:%=%.o) MODDIR := $(LIBDIR)/modules CONF_TARGET:= $(CONFDIR_REAL)/$(CONFFILE) PERM_TARGET:= $(PERMDIR_REAL)/$(PERMFILE) -DEFINES:=-DAMI_WITH_SSL '-DPROXY_VERSION="$(VERSION)"' '-DCDIR="$(CONFDIR)"' '-DCFILE="$(CONFFILE)"' +DEFINES:='-DPROXY_VERSION="$(VERSION)"' '-DCDIR="$(CONFDIR)"' '-DCFILE="$(CONFFILE)"' DEFINES+='-DMDIR="$(MODDIR)"' '-DPDIR="$(PERMDIR)"' '-DPFILE="$(PERMFILE)"' VPATH = src diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 25aab23..ac30b1b 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -374,8 +374,8 @@ int ConnectAsterisk(struct mansession *s) { s->connected = 0; if (debug) - debugmsg("asterisk@%s: Connecting (u=%s, p=%s)", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), - s->server->ast_user, s->server->ast_pass); + debugmsg("asterisk@%s: Connecting (u=%s, p=%s, ssl=%s)", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), + s->server->ast_user, s->server->ast_pass, s->server->use_ssl ? "on" : "off"); /* Construct auth message just once */ memset( &m, 0, sizeof(struct message) ); @@ -385,12 +385,7 @@ int ConnectAsterisk(struct mansession *s) { AddHeader(&m, "Events: %s", s->server->ast_events); for ( ;; ) { - if ( connect_nonb(s->fd, (struct sockaddr *) &s->sin, sizeof(s->sin), 2) < 0 ) { - if (errno == EISCONN) { - pthread_mutex_lock(&s->lock); - s->fd = socket(AF_INET, SOCK_STREAM, 0); - pthread_mutex_unlock(&s->lock); - } + if ( ast_connect(s) == -1 ) { if (debug) debugmsg("asterisk@%s: Connect failed, Retrying (%d) %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), r, strerror(errno) ); @@ -457,7 +452,6 @@ int StartServer(struct ast_server *srv) { debugmsg("Set %s output format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->output->formatname); } - if (pthread_create(&s->t, &attr, (void *)HandleAsterisk, s)) destroy_session(s); else @@ -673,6 +667,9 @@ int main(int argc, char *argv[]) sessions = NULL; LaunchAsteriskThreads(); + /* Initialize SSL Client-Side Context */ + client_init_secure(); + /* Setup listener socket to setup new sessions... */ if ((asock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { fprintf(stderr,"Cannot create listener socket!\n"); diff --git a/src/common.c b/src/common.c index f4b370e..d6bea7a 100644 --- a/src/common.c +++ b/src/common.c @@ -102,60 +102,6 @@ const char *ast_inet_ntoa(char *buf, int bufsiz, struct in_addr ia) } -int connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec) -{ - int flags, n, error; - socklen_t len; - fd_set rset, wset; - struct timeval tval; - - flags = fcntl(sockfd, F_GETFL, 0); - fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); - - error = 0; - if ( (n = connect(sockfd, (struct sockaddr *) saptr, salen)) < 0) - if (errno != EINPROGRESS) - return(-1); - - /* Do whatever we want while the connect is taking place. */ - - if (n == 0) - goto done; /* connect completed immediately */ - - FD_ZERO(&rset); - FD_SET(sockfd, &rset); - wset = rset; - tval.tv_sec = nsec; - tval.tv_usec = 0; - - if ( (n = select(sockfd+1, &rset, &wset, NULL, - nsec ? &tval : NULL)) == 0) { - /*close(sockfd);*/ /* we want to retry */ - errno = ETIMEDOUT; - return(-1); - } - - if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { - len = sizeof(error); - if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) - return(-1); /* Solaris pending error */ - } else { - /*err_quit("select error: sockfd not set");*/ - logmsg("select error: sockfd not set"); - return(-1); - } - -done: - fcntl(sockfd, F_SETFL, flags); /* restore file status flags */ - - if (error) { - /* close(sockfd); */ /* we want to retry... */ - errno = error; - return(-1); - } - return(0); -} - /*! If you are calling ast_carefulwrite, it is assumed that you are calling it on a file descriptor that _DOES_ have NONBLOCK set. This way, there is only one system call made to do a write, unless we actually diff --git a/src/config.c b/src/config.c index 02d76ec..bc1d2cb 100644 --- a/src/config.c +++ b/src/config.c @@ -9,6 +9,7 @@ void *add_server(char *srvspec) { int ccount = 0; struct ast_server *srv; char *s; + char usessl[10]; /* malloc ourselves a server credentials structure */ srv = malloc(sizeof(struct ast_server)); @@ -17,6 +18,7 @@ void *add_server(char *srvspec) { exit(1); } memset(srv, 0, sizeof (struct ast_server) ); + memset(usessl, 0, sizeof (usessl) ); s = srvspec; do { @@ -42,20 +44,22 @@ void *add_server(char *srvspec) { strncat(srv->ast_events, s, 1); break; case 5: - srv->use_ssl = strcmp(s,"yes") ? 0 : 1; + strncat(usessl, s, 1); break; } } while (*(s++)); - if (!srv->ast_host || !srv->ast_port || !srv->ast_user || !srv->ast_pass || !srv->ast_events || !srv->use_ssl) { - fprintf(stderr, "Aborting! Server spec incomplete: %s!\n", srvspec); + + if (!*srv->ast_host || !*srv->ast_port || !*srv->ast_user || !*srv->ast_pass || !*srv->ast_events || !*usessl) { + fprintf(stderr, "Aborting: server spec incomplete: %s\n", srvspec); free(srv); exit(1); - } else { - srv->next = pc.serverlist; - pc.serverlist = srv; } + srv->use_ssl = (!strcmp(usessl,"on")); + srv->next = pc.serverlist; + pc.serverlist = srv; + return 0; /* TODO: make sure server credentials are freed at leave */ } diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index 1d7ec47..fa57126 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -134,6 +134,5 @@ int proxyerror_do(struct mansession *s, char *err); int get_input(struct mansession *s, char *output); int SetIOHandlers(struct mansession *s, char *ifmt, char *ofmt); void destroy_session(struct mansession *s); -int connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec); int ast_carefulwrite(int fd, char *s, int len, int timeoutms); extern void *SendError(struct mansession *s, char *errmsg); diff --git a/src/ssl.c b/src/ssl.c index 265c961..ce38814 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -39,7 +39,6 @@ You must check for (value==-1) instead, since all other negative fd's now are valid fd's. */ -#ifdef AMI_WITH_SSL #include #include #include @@ -58,6 +57,7 @@ #include "ssl.h" SSL_CTX *sctx; +SSL_CTX *cctx; static long rec_bytes; static long sent_bytes; static int ssl_initialized; @@ -95,6 +95,25 @@ int init_secure(char *certfile) return 0; } + +/* Initializes all the client-side ssl related stuff here. +*/ +int client_init_secure(void) +{ + SSL_METHOD *meth; + + /* client init */ + SSLeay_add_ssl_algorithms(); + meth = SSLv23_client_method(); + SSL_load_error_strings(); + cctx = SSL_CTX_new (meth); + + if (!cctx) { + fprintf(stderr, "Failed to create a client ssl context!\n"); + } + return 0; +} + /*! \brief Takes the negative ssl fd and returns the positive fd recieved from the os. * It goes through arrray of fixed maximum number of secured channels. */ @@ -307,4 +326,62 @@ int is_encrypt_request(int sslclhellotimeout, int fd) } return 0; } -#endif + + +/* Connects to an asterisk server either plain or SSL as appropriate +*/ +int ast_connect(struct mansession *a) { + int s, err=-1, fd; + SSL* ssl; + + fd = connect_nonb(a); + if ( fd < 0 ) + return -1; + + if (a->server->use_ssl) { + if ((s=sec_getslot())!=-1) { /* find a slot for the ssl handle */ + sec_channel[s].fd = fd; /* remember the real fd */ + + if((ssl=SSL_new(cctx))) { /* get a new ssl */ + sec_channel[s].ssl = ssl; + SSL_set_fd(ssl, fd); /* and attach the real fd */ + err = SSL_connect(ssl); /* now try and connect */ + } + fd = -(s+2); /* offset by two and negate */ + /* this tells us it is a ssl fd */ + } + + if (err==-1) { + close_sock(fd); /* that frees the ssl too */ + fd = -1; + } + } + + pthread_mutex_lock(&a->lock); + a->fd = fd; + pthread_mutex_unlock(&a->lock); + + return fd; +} + +int connect_nonb(struct mansession *a) +{ + int s, flags, one=1; + + s = get_real_fd(a->fd); + + flags = fcntl(s, F_GETFL, 0); + fcntl(s, F_SETFL, flags | O_NONBLOCK); + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))==-1) { + (void)close(s); + return -1; + } + + if (connect(s, (struct sockaddr *) &a->sin, sizeof(a->sin)) < 0) { + if (errno != EINPROGRESS) + return -1; + } + return s; +} + -- cgit From 6c9d46845fbaa89dc5481fb8d0d1c78728628122 Mon Sep 17 00:00:00 2001 From: David Troy Date: Tue, 4 Apr 2006 20:38:07 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@80 f02b47b9-160a-0410-81a6-dc3441afb0ec --- TODO | 2 ++ src/astmanproxy.c | 6 +++--- src/include/ssl.h | 2 +- src/ssl.c | 17 ++++++++++------- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/TODO b/TODO index 5febf5a..439c6d8 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ +fix non-blocking in ast_connect + 'connected' flag; do not set until we get "authentication accepted' back from server Check for module versions; do not run without modules installed use a key? see loader.c in * diff --git a/src/astmanproxy.c b/src/astmanproxy.c index ac30b1b..9481610 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -663,13 +663,13 @@ int main(int argc, char *argv[]) /* Read initial state for user permissions */ ReadPerms(); + /* Initialize SSL Client-Side Context */ + client_init_secure(); + /* Initialize global client/server list */ sessions = NULL; LaunchAsteriskThreads(); - /* Initialize SSL Client-Side Context */ - client_init_secure(); - /* Setup listener socket to setup new sessions... */ if ((asock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { fprintf(stderr,"Cannot create listener socket!\n"); diff --git a/src/include/ssl.h b/src/include/ssl.h index a52b424..19c74a7 100644 --- a/src/include/ssl.h +++ b/src/include/ssl.h @@ -28,7 +28,7 @@ extern "C" { The negative fd is used as an index into this data structure (after processing). Choose SEC_MAX to be impossibly large for the application. */ -#define SEC_MAX 8 +#define SEC_MAX 16 struct { int fd; SSL* ssl; diff --git a/src/ssl.c b/src/ssl.c index ce38814..c77d57a 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -108,9 +108,10 @@ int client_init_secure(void) SSL_load_error_strings(); cctx = SSL_CTX_new (meth); - if (!cctx) { - fprintf(stderr, "Failed to create a client ssl context!\n"); - } + if (!cctx) + debugmsg("Failed to create a client ssl context!"); + else + debugmsg("Client SSL Context Initialized"); return 0; } @@ -346,10 +347,12 @@ int ast_connect(struct mansession *a) { sec_channel[s].ssl = ssl; SSL_set_fd(ssl, fd); /* and attach the real fd */ err = SSL_connect(ssl); /* now try and connect */ - } + } else + debugmsg("couldn't create ssl client context"); fd = -(s+2); /* offset by two and negate */ /* this tells us it is a ssl fd */ - } + } else + debugmsg("couldn't get SSL slot!"); if (err==-1) { close_sock(fd); /* that frees the ssl too */ @@ -370,8 +373,8 @@ int connect_nonb(struct mansession *a) s = get_real_fd(a->fd); - flags = fcntl(s, F_GETFL, 0); - fcntl(s, F_SETFL, flags | O_NONBLOCK); +/* flags = fcntl(s, F_GETFL, 0); + fcntl(s, F_SETFL, flags | O_NONBLOCK); */ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))==-1) { (void)close(s); -- cgit From ba8c28c63e3a0713a810be04dcd96129e61a08a4 Mon Sep 17 00:00:00 2001 From: David Troy Date: Tue, 4 Apr 2006 22:26:33 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@81 f02b47b9-160a-0410-81a6-dc3441afb0ec --- README | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README b/README index 7369dd2..eab3ac1 100644 --- a/README +++ b/README @@ -1,4 +1,5 @@ -astmanproxy README (c) 2005 David C. Troy +astmanproxy README +(c) 2005-2006 David C. Troy ------------------------------------------------------------------ FOREWORD & QUICK START @@ -16,9 +17,10 @@ has since evolved into a full multithreaded quasi-stateful proxy based in c/pthreads. It is now capable of serving as the basis for an extensible application framework for multiple Asterisk servers. -New features in version 1.1 include: +Features include: - Multiple Input/Output formats: HTTP, XML, CSV, and Standard + - SSL Support (including HTTPS) - API for addition of new, modular I/O formats - Ability to support communication with multiple Asterisk Servers - I/O Formats selectable on a per-client basis -- cgit From d9e53b2987116fa0d335428eabb5c0d35ba11017 Mon Sep 17 00:00:00 2001 From: David Troy Date: Tue, 4 Apr 2006 22:27:38 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@82 f02b47b9-160a-0410-81a6-dc3441afb0ec --- TODO | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index 439c6d8..65a0a27 100644 --- a/TODO +++ b/TODO @@ -10,4 +10,5 @@ libtool/autoconf/automake support SetInputFormat proxyaction? clean up autodisconnect vs. inputcomplete/outputcomplete -deal with no empty http better (no formdata; return error header?) +deal with http non-conformity better +SOAP handler? -- cgit From 08a84749d9c92829d580c508f3b0256f7b188321 Mon Sep 17 00:00:00 2001 From: David Troy Date: Tue, 4 Apr 2006 22:33:15 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@83 f02b47b9-160a-0410-81a6-dc3441afb0ec --- TODO | 1 + src/astmanproxy.c | 5 +---- src/config.c | 1 - src/proxyfunc.c | 4 ---- 4 files changed, 2 insertions(+), 9 deletions(-) diff --git a/TODO b/TODO index 65a0a27..bb0f233 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,5 @@ fix non-blocking in ast_connect +kill autodisconnect concept; this would be managed by inputcomplete/outputcomplete in drivers? 'connected' flag; do not set until we get "authentication accepted' back from server Check for module versions; do not run without modules installed diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 9481610..dbbb05c 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -344,8 +344,6 @@ void *HandleAsterisk(struct mansession *s) if (!WriteClients(&m)) break; - /* TODO: does it make any sense to abort * conn if we cannot write to clients? I don't think so. - Do we even get a return code back that means anything? I don't think so. */ } else if (res < 0) { if (debug) debugmsg("asterisk@%s: Not connected", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); @@ -490,14 +488,13 @@ int SetIOHandlers(struct mansession *s, char *ifmt, char *ofmt) io = io->next; } + /* set default handlers if non match was found */ if (!s->output) { - /* TODO: Output debug that default/first handler was used */ s->output = iohandlers; res = 1; } if (!s->input) { - /* TODO: Output debug that default/first handler was used */ s->input = iohandlers; res = 1; } diff --git a/src/config.c b/src/config.c index bc1d2cb..b7bbdc7 100644 --- a/src/config.c +++ b/src/config.c @@ -61,7 +61,6 @@ void *add_server(char *srvspec) { pc.serverlist = srv; return 0; - /* TODO: make sure server credentials are freed at leave */ } void *processline(char *s) { diff --git a/src/proxyfunc.c b/src/proxyfunc.c index 54df298..d1d5f38 100644 --- a/src/proxyfunc.c +++ b/src/proxyfunc.c @@ -54,7 +54,6 @@ void *ProxySetOutputFormat(struct mansession *s, struct message *m) { value = astman_get_header(m, "OutputFormat"); SetIOHandlers(s, s->input->formatname, value); - /* TODO: this is retarded */ memset(&mo, 0, sizeof(struct message)); AddHeader(&mo, "ProxyResponse: Success"); @@ -206,10 +205,7 @@ int ProxyAddServer(struct mansession *s, struct message *m) { fprintf(stderr, "Failed to allocate server credentials: %s\n", strerror(errno)); exit(1); } - memset(srv, 0, sizeof (struct ast_server) ); - - /* TODO: Disallow adding of duplicate servers? Or not, I suppose that could be useful (events on/off) */ memset(srv, 0, sizeof(struct ast_server) ); memset(&mo, 0, sizeof(struct message)); strcpy(srv->ast_host, astman_get_header(m, "Server")); -- cgit From b927204db9c8163edbfc17f8e9ceee7f8052c08d Mon Sep 17 00:00:00 2001 From: David Troy Date: Tue, 4 Apr 2006 22:43:25 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@84 f02b47b9-160a-0410-81a6-dc3441afb0ec --- README | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README b/README index eab3ac1..8c5578e 100644 --- a/README +++ b/README @@ -13,14 +13,15 @@ proxy of some kind. Why? A proxy offers: - Less connections and networking load for asterisk This proxy began as the the perl/select based "simpleproxy.pl" and -has since evolved into a full multithreaded quasi-stateful proxy -based in c/pthreads. It is now capable of serving as the basis for -an extensible application framework for multiple Asterisk servers. +has since evolved into a full multithreaded stateful proxy written +in c/pthreads. It can serve as the basis for an extensible +application framework for communication with multiple Asterisk +servers. Features include: - Multiple Input/Output formats: HTTP, XML, CSV, and Standard - - SSL Support (including HTTPS) + - SSL Support for clients & servers (including HTTPS clients) - API for addition of new, modular I/O formats - Ability to support communication with multiple Asterisk Servers - I/O Formats selectable on a per-client basis -- cgit From 8c46813c678ff0ca734e7b2866623a61109f26aa Mon Sep 17 00:00:00 2001 From: David Troy Date: Tue, 4 Apr 2006 22:53:29 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@85 f02b47b9-160a-0410-81a6-dc3441afb0ec --- perl/soapclient.pl | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100755 perl/soapclient.pl diff --git a/perl/soapclient.pl b/perl/soapclient.pl new file mode 100755 index 0000000..2ab9c2f --- /dev/null +++ b/perl/soapclient.pl @@ -0,0 +1,11 @@ +#!/usr/bin/perl -w + +use SOAP::Lite; + +print SOAP::Lite + -> uri('http://www.soaplite.com/Demo') + -> proxy('http://services.soaplite.com/hibye.cgi') + -> hi() + -> result . "\n"; + +exit(0); -- cgit From 8c5e24cab7e89f74bbc363223fe00dd45edb28e2 Mon Sep 17 00:00:00 2001 From: David Troy Date: Tue, 4 Apr 2006 23:01:38 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@86 f02b47b9-160a-0410-81a6-dc3441afb0ec --- TODO | 2 +- perl/soapclient.pl | 6 +- src/soap.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 src/soap.c diff --git a/TODO b/TODO index bb0f233..8083f1c 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,6 @@ fix non-blocking in ast_connect kill autodisconnect concept; this would be managed by inputcomplete/outputcomplete in drivers? +finish SOAP handler 'connected' flag; do not set until we get "authentication accepted' back from server Check for module versions; do not run without modules installed @@ -12,4 +13,3 @@ SetInputFormat proxyaction? clean up autodisconnect vs. inputcomplete/outputcomplete deal with http non-conformity better -SOAP handler? diff --git a/perl/soapclient.pl b/perl/soapclient.pl index 2ab9c2f..4fb4d0f 100755 --- a/perl/soapclient.pl +++ b/perl/soapclient.pl @@ -3,9 +3,9 @@ use SOAP::Lite; print SOAP::Lite - -> uri('http://www.soaplite.com/Demo') - -> proxy('http://services.soaplite.com/hibye.cgi') - -> hi() + -> uri('http://localhost:1234/astmanproxy') + -> proxy('http://localhost:1234/') + -> astman('Action: Ping') -> result . "\n"; exit(0); diff --git a/src/soap.c b/src/soap.c new file mode 100644 index 0000000..8a8d245 --- /dev/null +++ b/src/soap.c @@ -0,0 +1,157 @@ +/* Asterisk Manager Proxy + Copyright (c) 2005 David C. Troy + + This program is free software, distributed under the terms of + the GNU General Public License. + + HTTP Input Handler +*/ + +#include "astmanproxy.h" + +int ParseHTTPInput(char *buf, struct message *m) { + char *n, *v; + + n = buf; + while ( (v = strstr(n, "=")) ) { + v += 1; + debugmsg("n: %s, v: %s", n, v); + strncat(m->headers[m->hdrcount], n, v-n-1); + strcat(m->headers[m->hdrcount], ": "); + + if ( (n = strstr(v, "&")) ) { + n += 1; + } else { + n = (v + strlen(v) + 1); + } + strncat(m->headers[m->hdrcount], v, n-v-1); + debugmsg("got hdr: %s", m->headers[m->hdrcount]); + m->hdrcount++; + } + + return (m->hdrcount > 0); +} + +int HTTPHeader(struct mansession *s, char *status) { + + + time_t t; + struct tm tm; + char date[80]; + char ctype[15], hdr[MAX_LEN]; + + time(&t); + localtime_r(&t, &tm); + strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm); + + if ( !strcasecmp("xml", s->output->formatname) ) + sprintf(ctype, "text/xml"); + else + sprintf(ctype, "text/plain"); + + if (!strcmp("200 OK", status) ) + sprintf(hdr, + "HTTP/1.1 %s\r\n" + "Date: %s\r\n" + "Content-Type: %s\r\n" + "Connection: close\r\n" + "Server: %s/%s\r\n\r\n", status, + date, ctype, PROXY_BANNER, PROXY_VERSION); + else + sprintf(hdr, + "HTTP/1.1 %s\r\n" + "Date: %s\r\n" + "Status: %s\r\n" + "Server: %s/%s\r\n\r\n", status, date, status, PROXY_BANNER, PROXY_VERSION); + + pthread_mutex_lock(&s->lock); + s->inputcomplete = 1; + ast_carefulwrite(s->fd, hdr, strlen(hdr), s->writetimeout); + pthread_mutex_unlock(&s->lock); + debugmsg("http header: %s", hdr); + + return 0; +} + +int _read(struct mansession *s, struct message *m) { + + /* Note: No single line may be longer than MAX_LEN/s->inbuf, as per get_input */ + /* No HTTP Input may be longer than BUFSIZE */ + + char line[MAX_LEN], method[10], formdata[MAX_LEN], status[15]; + int res, clength = 0; + + memset(method, 0, sizeof method); + memset(formdata, 0, sizeof formdata); + memset(status, 0, sizeof status); + + /* for http, don't do get_input forever */ + for (;;) { + + if (s->inputcomplete && !s->outputcomplete) + continue; + else if (s->inputcomplete && s->outputcomplete) + return -1; + + memset(line, 0, sizeof line); + res = get_input(s, line); + debugmsg("res=%d, line: %s",res, line); + + if (res > 0) { + debugmsg("Got http: %s", line); + + if ( !clength && !strncasecmp(line, "Content-Length: ", 16) ) + clength = atoi(line+16); + + if (!*method) { + if ( !strncmp(line,"POST",4) ) { + strncpy(method, line, 4); + } else if ( !strncmp(line,"GET",3)) { + if ( strlen(line) > 14 ) { + /* GET / HTTP/1.1 ---- this is bad */ + /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */ + strncpy(method, line, 3); + memcpy(formdata, line+6, strstr(line, " HTTP")-line-6); + sprintf(status, "200 OK"); + } else + sprintf(status, "501 Not Implemented"); + } + } + } else if (res == 0) { + if (*method && !*formdata) { + if ( !strcasecmp(method, "POST") && clength && s->inlen==clength) { + pthread_mutex_lock(&s->lock); + strncpy(formdata, s->inbuf, clength); + s->inlen = 0; + pthread_mutex_unlock(&s->lock); + sprintf(status, "200 OK"); + } + } + } + + if (res < 0) + break; + + if (*status) { + HTTPHeader(s, status); + + /* now, let's transform and copy into a standard message block */ + if (!strcmp("200 OK", status) ) { + res = ParseHTTPInput(formdata, m); + return res; + } else { + pthread_mutex_lock(&s->lock); + s->outputcomplete = 1; + pthread_mutex_unlock(&s->lock); + return 0; + } + } + } + return -1; +} + +int _autodisconnect() { + return 1; +} + +/* We do not define a _write or _onconnect method */ -- cgit From e61f8f336a27fb32280bd9bd5efdee9d6e0d2781 Mon Sep 17 00:00:00 2001 From: David Troy Date: Tue, 4 Apr 2006 23:12:18 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@87 f02b47b9-160a-0410-81a6-dc3441afb0ec --- perl/soapclient.pl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/perl/soapclient.pl b/perl/soapclient.pl index 4fb4d0f..76c766c 100755 --- a/perl/soapclient.pl +++ b/perl/soapclient.pl @@ -1,4 +1,10 @@ #!/usr/bin/perl -w +# +# This is for testing only; the thought is to add content handlers +# to http.c to deal with 1) REST (default), 2) XML/XMLRPC, +# 3) SOAP http data input +# +# Do not expect this to do anything right now use SOAP::Lite; -- cgit From 012c2fcfbfec0a7d2676c393a9fdc9fee74fa09b Mon Sep 17 00:00:00 2001 From: David Troy Date: Tue, 4 Apr 2006 23:24:34 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@88 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/http.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/http.c b/src/http.c index 8a8d245..8fd5520 100644 --- a/src/http.c +++ b/src/http.c @@ -118,6 +118,8 @@ int _read(struct mansession *s, struct message *m) { } } } else if (res == 0) { + /* x-www-form-urlencoded handler */ + /* Content-Type: application/x-www-form-urlencoded */ if (*method && !*formdata) { if ( !strcasecmp(method, "POST") && clength && s->inlen==clength) { pthread_mutex_lock(&s->lock); -- cgit From ca4ed8c2cbec7db4d9001e4f5725dcb3ea71399a Mon Sep 17 00:00:00 2001 From: David Troy Date: Thu, 6 Apr 2006 22:30:00 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@89 f02b47b9-160a-0410-81a6-dc3441afb0ec --- TODO | 2 + perl/soapclient.pl | 17 ------ src/astmanproxy.c | 9 +-- src/soap.c | 157 ----------------------------------------------------- src/ssl.c | 73 +++++++++++++++++++++---- 5 files changed, 68 insertions(+), 190 deletions(-) delete mode 100755 perl/soapclient.pl delete mode 100644 src/soap.c diff --git a/TODO b/TODO index 8083f1c..d360db5 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,6 @@ fix non-blocking in ast_connect +fix reconnect to lost asterisk server (socket reuse) +check s->connect construct kill autodisconnect concept; this would be managed by inputcomplete/outputcomplete in drivers? finish SOAP handler diff --git a/perl/soapclient.pl b/perl/soapclient.pl deleted file mode 100755 index 76c766c..0000000 --- a/perl/soapclient.pl +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/perl -w -# -# This is for testing only; the thought is to add content handlers -# to http.c to deal with 1) REST (default), 2) XML/XMLRPC, -# 3) SOAP http data input -# -# Do not expect this to do anything right now - -use SOAP::Lite; - -print SOAP::Lite - -> uri('http://localhost:1234/astmanproxy') - -> proxy('http://localhost:1234/') - -> astman('Action: Ping') - -> result . "\n"; - -exit(0); diff --git a/src/astmanproxy.c b/src/astmanproxy.c index dbbb05c..a22f1b8 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -120,7 +120,7 @@ void leave(int sig) { void Version( void ) { - printf("astmanproxy: Version %s, (C) David C. Troy\n", PROXY_VERSION); + printf("astmanproxy: Version %s, (C) David C. Troy 2005-2006\n", PROXY_VERSION); return; } @@ -345,7 +345,8 @@ void *HandleAsterisk(struct mansession *s) if (!WriteClients(&m)) break; } else if (res < 0) { - if (debug) + /* TODO: do we need to do more than this here? or something different? */ + if ( debug ) debugmsg("asterisk@%s: Not connected", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); if ( ConnectAsterisk(s) ) break; @@ -385,8 +386,8 @@ int ConnectAsterisk(struct mansession *s) { for ( ;; ) { if ( ast_connect(s) == -1 ) { if (debug) - debugmsg("asterisk@%s: Connect failed, Retrying (%d) %s", - ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), r, strerror(errno) ); + debugmsg("asterisk@%s: Connect failed, Retrying (%d) %s [%d]", + ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), r, strerror(errno), errno ); if (pc.maxretries && (++r>pc.maxretries) ) { res = 1; break; diff --git a/src/soap.c b/src/soap.c deleted file mode 100644 index 8a8d245..0000000 --- a/src/soap.c +++ /dev/null @@ -1,157 +0,0 @@ -/* Asterisk Manager Proxy - Copyright (c) 2005 David C. Troy - - This program is free software, distributed under the terms of - the GNU General Public License. - - HTTP Input Handler -*/ - -#include "astmanproxy.h" - -int ParseHTTPInput(char *buf, struct message *m) { - char *n, *v; - - n = buf; - while ( (v = strstr(n, "=")) ) { - v += 1; - debugmsg("n: %s, v: %s", n, v); - strncat(m->headers[m->hdrcount], n, v-n-1); - strcat(m->headers[m->hdrcount], ": "); - - if ( (n = strstr(v, "&")) ) { - n += 1; - } else { - n = (v + strlen(v) + 1); - } - strncat(m->headers[m->hdrcount], v, n-v-1); - debugmsg("got hdr: %s", m->headers[m->hdrcount]); - m->hdrcount++; - } - - return (m->hdrcount > 0); -} - -int HTTPHeader(struct mansession *s, char *status) { - - - time_t t; - struct tm tm; - char date[80]; - char ctype[15], hdr[MAX_LEN]; - - time(&t); - localtime_r(&t, &tm); - strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm); - - if ( !strcasecmp("xml", s->output->formatname) ) - sprintf(ctype, "text/xml"); - else - sprintf(ctype, "text/plain"); - - if (!strcmp("200 OK", status) ) - sprintf(hdr, - "HTTP/1.1 %s\r\n" - "Date: %s\r\n" - "Content-Type: %s\r\n" - "Connection: close\r\n" - "Server: %s/%s\r\n\r\n", status, - date, ctype, PROXY_BANNER, PROXY_VERSION); - else - sprintf(hdr, - "HTTP/1.1 %s\r\n" - "Date: %s\r\n" - "Status: %s\r\n" - "Server: %s/%s\r\n\r\n", status, date, status, PROXY_BANNER, PROXY_VERSION); - - pthread_mutex_lock(&s->lock); - s->inputcomplete = 1; - ast_carefulwrite(s->fd, hdr, strlen(hdr), s->writetimeout); - pthread_mutex_unlock(&s->lock); - debugmsg("http header: %s", hdr); - - return 0; -} - -int _read(struct mansession *s, struct message *m) { - - /* Note: No single line may be longer than MAX_LEN/s->inbuf, as per get_input */ - /* No HTTP Input may be longer than BUFSIZE */ - - char line[MAX_LEN], method[10], formdata[MAX_LEN], status[15]; - int res, clength = 0; - - memset(method, 0, sizeof method); - memset(formdata, 0, sizeof formdata); - memset(status, 0, sizeof status); - - /* for http, don't do get_input forever */ - for (;;) { - - if (s->inputcomplete && !s->outputcomplete) - continue; - else if (s->inputcomplete && s->outputcomplete) - return -1; - - memset(line, 0, sizeof line); - res = get_input(s, line); - debugmsg("res=%d, line: %s",res, line); - - if (res > 0) { - debugmsg("Got http: %s", line); - - if ( !clength && !strncasecmp(line, "Content-Length: ", 16) ) - clength = atoi(line+16); - - if (!*method) { - if ( !strncmp(line,"POST",4) ) { - strncpy(method, line, 4); - } else if ( !strncmp(line,"GET",3)) { - if ( strlen(line) > 14 ) { - /* GET / HTTP/1.1 ---- this is bad */ - /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */ - strncpy(method, line, 3); - memcpy(formdata, line+6, strstr(line, " HTTP")-line-6); - sprintf(status, "200 OK"); - } else - sprintf(status, "501 Not Implemented"); - } - } - } else if (res == 0) { - if (*method && !*formdata) { - if ( !strcasecmp(method, "POST") && clength && s->inlen==clength) { - pthread_mutex_lock(&s->lock); - strncpy(formdata, s->inbuf, clength); - s->inlen = 0; - pthread_mutex_unlock(&s->lock); - sprintf(status, "200 OK"); - } - } - } - - if (res < 0) - break; - - if (*status) { - HTTPHeader(s, status); - - /* now, let's transform and copy into a standard message block */ - if (!strcmp("200 OK", status) ) { - res = ParseHTTPInput(formdata, m); - return res; - } else { - pthread_mutex_lock(&s->lock); - s->outputcomplete = 1; - pthread_mutex_unlock(&s->lock); - return 0; - } - } - } - return -1; -} - -int _autodisconnect() { - return 1; -} - -/* We do not define a _write or _onconnect method */ diff --git a/src/ssl.c b/src/ssl.c index c77d57a..9f9ff0f 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -340,6 +340,7 @@ int ast_connect(struct mansession *a) { return -1; if (a->server->use_ssl) { + debugmsg("initiating ssl connection"); if ((s=sec_getslot())!=-1) { /* find a slot for the ssl handle */ sec_channel[s].fd = fd; /* remember the real fd */ @@ -360,6 +361,7 @@ int ast_connect(struct mansession *a) { } } + debugmsg("returning ast_connect with %d", fd); pthread_mutex_lock(&a->lock); a->fd = fd; pthread_mutex_unlock(&a->lock); @@ -369,22 +371,69 @@ int ast_connect(struct mansession *a) { int connect_nonb(struct mansession *a) { - int s, flags, one=1; + int flags, n, error; + socklen_t len; + fd_set rset, wset; + struct timeval tval; + int nsec = 1, sockfd; + + sockfd = get_real_fd(a->fd); + + flags = fcntl(sockfd, F_GETFL, 0); + fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + + error = 0; + if ( (n = connect(sockfd, (struct sockaddr *) &a->sin, sizeof(a->sin)) ) < 0 ) { + /* TODO: This seems like the nine pound hammer to me... */ + /* perhaps something a bit more elegant; errno seems to change too */ + if (errno == EISCONN || errno == 103 || errno==111) { + debugmsg("connect_nonb: error %d, closing old fd and grabbing a new one...", errno); + /* looks like our old socket died, let's round up a new one and try again */ + close_sock(a->fd); + pthread_mutex_lock(&a->lock); + a->fd = socket(AF_INET, SOCK_STREAM, 0); + pthread_mutex_unlock(&a->lock); + return(-1); + } + if (errno != EINPROGRESS) + return(-1); + } - s = get_real_fd(a->fd); + /* Do whatever we want while the connect is taking place. */ -/* flags = fcntl(s, F_GETFL, 0); - fcntl(s, F_SETFL, flags | O_NONBLOCK); */ + if (n == 0) + goto done; /* connect completed immediately */ - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))==-1) { - (void)close(s); - return -1; + FD_ZERO(&rset); + FD_SET(sockfd, &rset); + wset = rset; + tval.tv_sec = nsec; + tval.tv_usec = 0; + + if ( (n = select(sockfd+1, &rset, &wset, NULL, + nsec ? &tval : NULL)) == 0) { + /*close(sockfd);*/ /* we want to retry */ + errno = ETIMEDOUT; + return(-1); } - if (connect(s, (struct sockaddr *) &a->sin, sizeof(a->sin)) < 0) { - if (errno != EINPROGRESS) - return -1; + if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { + len = sizeof(error); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) + return(-1); /* Solaris pending error */ + } else { + /*err_quit("select error: sockfd not set");*/ + logmsg("select error: sockfd not set"); + return(-1); } - return s; -} +done: + fcntl(sockfd, F_SETFL, flags); /* restore file status flags */ + + if (error) { + /* close(sockfd); */ /* disable for now, we want to retry... */ + errno = error; + return(-1); + } + return(sockfd); +} -- cgit From c38c05f5c7c93c8737227601a95500db9a5044cd Mon Sep 17 00:00:00 2001 From: David Troy Date: Fri, 7 Apr 2006 16:33:38 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@90 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/astmanproxy.c | 2 +- src/include/astmanproxy.h | 11 +++++++++++ src/include/ssl.h | 6 ++---- src/ssl.c | 1 - 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/astmanproxy.c b/src/astmanproxy.c index a22f1b8..0cc3a6a 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -168,7 +168,7 @@ void destroy_session(struct mansession *s) } int WriteClients(struct message *m) { - struct mansession *c, *d; + struct mansession *c; char *actionid; c = sessions; diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index fa57126..a8cb26b 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -136,3 +136,14 @@ int SetIOHandlers(struct mansession *s, char *ifmt, char *ofmt); void destroy_session(struct mansession *s); int ast_carefulwrite(int fd, char *s, int len, int timeoutms); extern void *SendError(struct mansession *s, char *errmsg); + +int close_sock(int socket); +int ProxyChallenge(struct mansession *s, struct message *m); +int ast_connect(struct mansession *a); +int is_encrypt_request(int sslclhellotimeout, int fd); +int saccept(int s); +int get_real_fd(int fd); +int client_init_secure(void); +int init_secure(char *certfile); +int m_send(int fd, const void *data, size_t len); +int m_recv(int s, void *buf, size_t len, int flags); diff --git a/src/include/ssl.h b/src/include/ssl.h index 19c74a7..123bd43 100644 --- a/src/include/ssl.h +++ b/src/include/ssl.h @@ -17,11 +17,9 @@ #define _SSL_ADDON_H_ #include +#include "astmanproxy.h" - -#ifdef __cplusplus -extern "C" { -#endif +int connect_nonb(struct mansession *a); /*! \brief This data structure holds the additional SSL data needed to use the ssl functions. diff --git a/src/ssl.c b/src/ssl.c index 9f9ff0f..9f5b341 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -53,7 +53,6 @@ #include #include -#include "astmanproxy.h" #include "ssl.h" SSL_CTX *sctx; -- cgit From 524502c0a8d5b6d8ec1f15ab456b20c1d33451ad Mon Sep 17 00:00:00 2001 From: David Troy Date: Fri, 7 Apr 2006 16:52:32 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@91 f02b47b9-160a-0410-81a6-dc3441afb0ec --- Makefile | 56 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index b9b4f92..7257bbc 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,9 @@ LIBDIR := $(DESTDIR)/usr/lib/astmanproxy CONFFILE := astmanproxy.conf PERMFILE := astmanproxy.users DISTDIR := /var/www/html/astmanproxy - +CERTDIR := /var/lib/asterisk/certs +PROXYCERT := $(CERTDIR)/proxy-server.pem +PROXYSSLCONF := $(CONFDIR)/proxy-ssl.conf CC := gcc #LIBS := -lpthread @@ -78,23 +80,53 @@ $(SOBJS): %.so: %.o SERIAL=0 cert: - if [ ! -f /var/lib/asterisk/certs/proxy-server.pem ]; then \ + if [ ! -f $(PROXYCERT) ]; then \ + umask 77 ; \ + PEM1=`/bin/mktemp /tmp/openssl.XXXXXX` ; \ + PEM2=`/bin/mktemp /tmp/openssl.XXXXXX` ; \ + if [ ! -f $(PROXYSSLCONF) ]; then \ + install ./configs/ssl.conf $(PROXYSSLCONF); \ + fi; \ + /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) -config $(PROXYSSLCONF) ; \ + mkdir -p $(CERTDIR); \ + cat $$PEM1 > $(PROXYCERT) ; \ + echo "" >> $(PROXYCERT) ; \ + cat $$PEM2 >> $(PROXYCERT) ; \ + rm $$PEM1 $$PEM2; \ + fi + +certificate: + createcert="1"; \ + if [ -f $(PROXYCERT) ]; then \ + echo -n "The certificate already exists, Do you really want to create new one(yes/no)?"; \ + read answer; \ + if [ "$$answer" = "yes" ]; then \ + echo "I am creating a new certificate, Old one is copied as server.pem.old ";\ + sudo cp /var/lib/asterisk/certs/server.pem /var/lib/asterisk/certs/server.pem.old; \ + elif [ "$$answer" = "no" ]; then \ + echo "Certificate already exists, I am not creating a new certificate,";\ + createcert="0"; \ + else \ + echo "You need to enter either yes or no"; \ + createcert="0"; \ + fi; \ + fi; \ + if [ "$$createcert" = "1" ]; then \ umask 77 ; \ PEM1=`/bin/mktemp /tmp/openssl.XXXXXX` ; \ PEM2=`/bin/mktemp /tmp/openssl.XXXXXX` ; \ - if [ ! -f /etc/asterisk/ssl.conf ]; then \ - install ./configs/ssl.conf /etc/asterisk/ssl.conf; \ + if [ ! -f $(PROXYSSLCONF) ]; then \ + install ./configs/ssl.conf $(PROXYSSLCONF); \ fi; \ - /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) -config /etc/asterisk/ssl.conf ; \ - cat $$PEM1 > $@ ; \ - echo "" >> $@ ; \ - cat $$PEM2 >> $@ ; \ - cat $@ > proxy-server.pem ;\ - mkdir -p /var/lib/asterisk/certs; \ - mv $@ /var/lib/asterisk/certs/proxy-server.pem; \ - rm $$PEM1 $$PEM2 $@; \ + /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) -config $(PROXYSSLCONF) ; \ + mkdir -p $(CERTDIR); \ + cat $$PEM1 > $(PROXYCERT) ; \ + echo "" >> $(PROXYCERT) ; \ + cat $$PEM2 >> $(PROXYCERT) ; \ + rm $$PEM1 $$PEM2; \ fi + install: uninstall all install -d $(BINDIR) install astmanproxy $(BINDIR) -- cgit From 2e59afd9c5cdfcc8fa3f7237668568e9c85a15a0 Mon Sep 17 00:00:00 2001 From: David Troy Date: Fri, 7 Apr 2006 17:27:01 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@92 f02b47b9-160a-0410-81a6-dc3441afb0ec --- INSTALL | 18 ++++++++++++++++-- TODO | 5 ++--- VERSIONS | 3 ++- src/astmanproxy.c | 2 +- src/config.c | 6 +----- src/csv.c | 3 --- src/http.c | 4 ---- src/include/astmanproxy.h | 1 - src/standard.c | 3 --- src/xml.c | 5 ----- 10 files changed, 22 insertions(+), 28 deletions(-) diff --git a/INSTALL b/INSTALL index d50a2c7..1842a67 100644 --- a/INSTALL +++ b/INSTALL @@ -1,13 +1,27 @@ Quick Install Instructions -------------------------- -It's easy: +Requirements: openssl, openssl-devel + +Installation is easy: 1. make 2. make install -3. run: +3. To run: astmanproxy -d (for debug mode) +astmanproxy -dddddd (for more debug info) astmanproxy (for background mode) +Other make actions: + +make cert Makes initial proxy server certificate +make certificate Forces remake of proxy server certificate) +make clean Remove all binary object files from source tree + +Please read README and docs/README.* for info on specific modules. + Enjoy! + +-------------------------------------------- +(C) 2005-2006 David C. Troy, dave@popvox.com diff --git a/TODO b/TODO index d360db5..79ce5b8 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,7 @@ -fix non-blocking in ast_connect -fix reconnect to lost asterisk server (socket reuse) check s->connect construct -kill autodisconnect concept; this would be managed by inputcomplete/outputcomplete in drivers? finish SOAP handler +clean up reconnect to lost asterisk server (socket reuse) +clean up debugmsg instances to add if (debug) 'connected' flag; do not set until we get "authentication accepted' back from server Check for module versions; do not run without modules installed diff --git a/VERSIONS b/VERSIONS index 9336dd0..9559284 100644 --- a/VERSIONS +++ b/VERSIONS @@ -1,4 +1,4 @@ -1.20pre Now using ast_carefulwrite for non-blocking writes +1.20pre Now using ast_carefulwrite for non-blocking writes to all sockets Added asteriskwritetimeout config setting for asterisk write timeout Added clientwritetimeout config setting for client write timeout Added support for astmanproxy.users user authentication (Steve Davies) @@ -7,6 +7,7 @@ Added writetimeout var to mansession structure for use by ast_carefulwrite Added SSL Support (Remco Treffkorn, Mahesh Karoshi, John Todd; Tello Corp) Added 'usessl' option for connecting to asterisk servers + Fixed proxykey pointer bug (Steve Davies) ------------------------------------------------------------------------------------------------ 1.1pre2 Completely Modularized and Abstracted Input/Output formats diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 0cc3a6a..9eb5ac6 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -266,7 +266,7 @@ void *session_do(struct mansession *s) if (res > 0) { /* Check for anything that requires proxy-side processing */ - if (pc.key && !s->authenticated) { + if (pc.key[0] != '\0' && !s->authenticated) { key = astman_get_header(&m, "ProxyKey"); if (!strcmp(key, pc.key) ) { pthread_mutex_lock(&s->lock); diff --git a/src/config.c b/src/config.c index b7bbdc7..c8c9d80 100644 --- a/src/config.c +++ b/src/config.c @@ -190,11 +190,7 @@ int LoadHandlers() { io->write = wh; if (och) io->onconnect = och; - io->autodisconnect = dlsym(dlhandle, "_autodisconnect"); - if ((error = dlerror()) != NULL) { - if (debug) - debugmsg("loading: note, %s_autodisconnect not defined; ignoring", fmt); - } + io->dlhandle = dlhandle; io->next = iohandlers; iohandlers = io; diff --git a/src/csv.c b/src/csv.c index fb26f3b..94d9a00 100644 --- a/src/csv.c +++ b/src/csv.c @@ -28,6 +28,3 @@ int _write(struct mansession *s, struct message *m) { return 0; } -int _autodisconnect() { - return 0; -} diff --git a/src/http.c b/src/http.c index 8fd5520..4f107db 100644 --- a/src/http.c +++ b/src/http.c @@ -152,8 +152,4 @@ int _read(struct mansession *s, struct message *m) { return -1; } -int _autodisconnect() { - return 1; -} - /* We do not define a _write or _onconnect method */ diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index a8cb26b..db370dc 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -80,7 +80,6 @@ struct iohandler { int (*read) (); int (*write) (); int (*onconnect) (); - int *(*autodisconnect)(void); char formatname[80]; void *dlhandle; struct iohandler *next; diff --git a/src/standard.c b/src/standard.c index 0b2bd17..9e8f200 100644 --- a/src/standard.c +++ b/src/standard.c @@ -68,6 +68,3 @@ int _onconnect(struct mansession *s, struct message *m) { return 0; } -int _autodisconnect() { - return 0; -} diff --git a/src/xml.c b/src/xml.c index 2f3f9d0..72dc4fb 100644 --- a/src/xml.c +++ b/src/xml.c @@ -98,11 +98,6 @@ int _write(struct mansession *s, struct message *m) { return 0; } -int _autodisconnect() { - return 0; -} - - /* Takes a single manager header line and converts xml entities */ void xml_quote_string(char *s, char *o) { -- cgit From ce35268086acbc958c33e8b39e2440c7ed1d2592 Mon Sep 17 00:00:00 2001 From: David Troy Date: Fri, 7 Apr 2006 19:26:46 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@93 f02b47b9-160a-0410-81a6-dc3441afb0ec --- src/astmanproxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/astmanproxy.c b/src/astmanproxy.c index 9eb5ac6..f6c0cd5 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -261,7 +261,7 @@ void *session_do(struct mansession *s) debugmsg("calling %s_read...", s->input->formatname); res = s->input->read(s, &m); if (debug > 3) - debugmsg("%s_read result =%d", s->input->formatname, res); + debugmsg("%s_read result = %d", s->input->formatname, res); m.session = s; if (res > 0) { -- cgit From 27aa7a3382cf0ed4973fa7e207d4ffc9733f69d5 Mon Sep 17 00:00:00 2001 From: David Troy Date: Fri, 7 Apr 2006 20:55:51 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@94 f02b47b9-160a-0410-81a6-dc3441afb0ec --- Makefile | 4 ++-- configs/astmanproxy.conf | 3 +++ src/astmanproxy.c | 7 +++++-- src/config.c | 2 ++ src/config_perms.c | 2 -- src/include/astmanproxy.h | 3 ++- src/proxyfunc.c | 9 +++++++-- 7 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 7257bbc..4b22697 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,7 @@ DEFINES+='-DMDIR="$(MODDIR)"' '-DPDIR="$(PERMDIR)"' '-DPFILE="$(PERMFILE)"' VPATH = src # Add -g below for debug/GDB symbols -CFLAGS+= $(DEFINES) -g -Wall -O2 -D_REENTRANT -fPIC -Isrc/include -I/usr/include/openssl -I- +CFLAGS+= $(DEFINES) -Wall -O2 -D_REENTRANT -fPIC -Isrc/include -I/usr/include/openssl -I- # For printing only SRCS := $(MODS:%=%.c) @@ -77,7 +77,7 @@ $(OBJS): %.o: %.c $(SOBJS): %.so: %.o $(CC) $(SOLINK) $< -o $@ -SERIAL=0 +SERIAL=`date "+%Y%m%d%H%M%S"` cert: if [ ! -f $(PROXYCERT) ]; then \ diff --git a/configs/astmanproxy.conf b/configs/astmanproxy.conf index 3a017ac..6164e9c 100644 --- a/configs/astmanproxy.conf +++ b/configs/astmanproxy.conf @@ -57,6 +57,9 @@ listenport = 1234 ; in place and well understood. ; proxykey = foobar +; Do we require authentication (either proxykey or astmanproxy.users entry)? +authrequired = no + ; user and group for proxy to run as; will NOT run as root! proc_user = nobody proc_group = nobody diff --git a/src/astmanproxy.c b/src/astmanproxy.c index f6c0cd5..b84fdad 100644 --- a/src/astmanproxy.c +++ b/src/astmanproxy.c @@ -280,14 +280,17 @@ void *session_do(struct mansession *s) actionid = astman_get_header(&m, ACTION_ID); action = astman_get_header(&m, "Action"); if ( !strcasecmp(action, "Login") ) - ProxyLogin(s, &m); + if (!s->authenticated) + ProxyLogin(s, &m); + else + break; else if ( !strcasecmp(action, "Logoff") ) ProxyLogoff(s); else if ( !strcasecmp(action, "Challenge") ) ProxyChallenge(s, &m); else if ( !(*proxyaction == '\0') ) proxyaction_do(proxyaction, &m, s); - else if ( ValidateAction(&m, s ,0) ) { + else if ( ValidateAction(&m, s, 0) ) { if ( !(*actionid == '\0') ) setactionid(actionid, &m, s); if ( !WriteAsterisk(&m) ) diff --git a/src/config.c b/src/config.c index c8c9d80..f642aa6 100644 --- a/src/config.c +++ b/src/config.c @@ -107,6 +107,8 @@ void *processline(char *s) { pc.clientwritetimeout = atoi(value); else if (!strcmp(name,"sslclienthellotimeout") ) pc.sslclhellotimeout = atoi(value); + else if (!strcmp(name,"authrequired") ) + pc.authrequired = strcmp(value,"yes") ? 0 : 1; else if (!strcmp(name,"acceptencryptedconnection") ) pc.acceptencryptedconnection = strcmp(value,"yes") ? 0 : 1; else if (!strcmp(name,"acceptunencryptedconnection") ) diff --git a/src/config_perms.c b/src/config_perms.c index 939d90f..4dbeeb0 100644 --- a/src/config_perms.c +++ b/src/config_perms.c @@ -1,5 +1,3 @@ -/* #include */ -/* #include */ #include "astmanproxy.h" extern pthread_mutex_t userslock; diff --git a/src/include/astmanproxy.h b/src/include/astmanproxy.h index db370dc..dc7ac87 100644 --- a/src/include/astmanproxy.h +++ b/src/include/astmanproxy.h @@ -61,7 +61,8 @@ struct proxyconfig { int listen_port; char inputformat[80]; char outputformat[80]; - int autofilter; + int autofilter; /* enable autofiltering? */ + int authrequired; /* is authentication required? */ char key[80]; char proc_user[40]; char proc_group[40]; diff --git a/src/proxyfunc.c b/src/proxyfunc.c index d1d5f38..434baff 100644 --- a/src/proxyfunc.c +++ b/src/proxyfunc.c @@ -156,10 +156,12 @@ void *ProxyLogin(struct mansession *s, struct message *m) { AddHeader(&mo, "Response: Success"); AddHeader(&mo, "Message: Authentication accepted"); s->output->write(s, &mo); - s->authenticated = 1; + pthread_mutex_lock(&s->lock); + s->authenticated = 1; strcpy(s->user.channel, pu->channel); strcpy(s->user.icontext, pu->icontext); strcpy(s->user.ocontext, pu->ocontext); + pthread_mutex_unlock(&s->lock); if( debug ) debugmsg("Login as: %s", user); break; @@ -171,11 +173,14 @@ void *ProxyLogin(struct mansession *s, struct message *m) { if( !pu ) { SendError(s, "Authentication failed"); + pthread_mutex_lock(&s->lock); s->authenticated = 0; + pthread_mutex_unlock(&s->lock); if( debug ) debugmsg("Login failed as: %s/%s", user, secret); } + return 0; } @@ -334,7 +339,7 @@ int ValidateAction(struct message *m, struct mansession *s, int inbound) { char *uchannel; char *ucontext; - if( !s->authenticated ) + if( pc.authrequired && !s->authenticated ) return 0; if( inbound ) -- cgit From 2b4352d5ebd67ece2a473e5e3b7f14d6af216dbd Mon Sep 17 00:00:00 2001 From: David Troy Date: Fri, 7 Apr 2006 21:12:37 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@95 f02b47b9-160a-0410-81a6-dc3441afb0ec --- README | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 94 insertions(+), 21 deletions(-) diff --git a/README b/README index 8c5578e..4a8b89d 100644 --- a/README +++ b/README @@ -29,8 +29,9 @@ Features include: For example, you can use Astmanproxy as a single point of contact to communicate with multiple Asterisk servers. -You can use Astmanproxy as the basis for a web-based application: -send it data using HTTP POST or HTTP GET, and receive XML output. +You can use Astmanproxy as the basis for a web-based application: +send it data using HTTP POST or HTTP GET, and receive XML output. +Or use HTTP POST and get Standard (text/plain) output back! No web server required! You can use Astmanproxy as an XML feed for a .NET program that keeps @@ -140,15 +141,19 @@ Server: (x.x.x.x|hostname) The proxy also intercepts the following Actions: Action: Login - Since the proxy logs in on behalf of your clients, and you are - using key-based or network-level security to secure the proxy, - logins should not be passed to asterisk. Ideally, your apps - will be written not to issue Logins in the first place, but - if you do, the proxy will intercept them and NOT pass them - to Asterisk. Instead, it will respond with an Authentication - Successful message, just as Asterisk would upon successful - authentication. Note it will do this regardless of information - supplied. + You can login to astmanproxy just as you would the Asterisk + Manager Interface. The user credentials are stored in + astmanproxy.users. + +Action: Challenge + Astmanproxy now supports the MD5 challenge authentication + mechanism. See section below for more information on + this authentication mechanism and how you can use it + in your applications to avoid having to send a password + over the internet, and instead use a MD5 challenge to + hash your password before sending. Note that this is + somewhat less of an issue with SSL support now enabled, + however, some apps require this mechanism, and we support it. Action: Logoff You don't want your applications logging the proxy off of @@ -163,23 +168,91 @@ Blank Commands blank command blocks. =================================================================== -Author Contact Info +On the Action: Challenge Authentication Mechanism -This code is intended primarily as a foundation for others to add -new features and capabilities going forward. While I will attempt -to keep up with it and add to it, I make no guarantees that I'll be -able to do that. My hope is that the wonderful asterisk community -will start making changes, and that those changes can be managed -through Digium's great CVS process. +John Todd wrote this excellent summary of the Action: Challenge +Authentication Mechanism, and it accurately describes the +implementation included in astmanproxy: -However, you can concact me at dave@popvox.com, and I will endeavor -to post the latest code here: +While the SSL encryption of the AMI is great, it's always a good +policy to never send passwords at all if you have an alternative. + After connecting to the AMI port, send this message: + + Action: Challenge + AuthType: MD5 + + You should receive a challenge string: + + Response: Success + Challenge: 125065091 + +Then, assuming that the manager username is "joebob" and the +password is "yoyodyne11", perform this on a shell line of a handy +UNIX system (you programmers will figure out how to do this with a +library call, I'm sure): + + bash-3.00# md5 -s 125065091yoyodyne11 + MD5 ("125065091yoyodyne11") = e83a9e59e7c8d1bb6554982275d05016 + bash-3.00# + + Now use this key to log in, so type this to the AMI: + + Action: Login + AuthType: MD5 + Username: joebob + Key: e83a9e59e7c8d1bb6554982275d05016 + + ...and you'll get: + + Response: Success + Message: Authentication accepted + +=================================================================== +Software Updates, Author Info, and How to Contribute + +Digium has graciously agreed to host the development of AstManProxy +on their Subversion Community Server. + +Latest releases can be found here: +http://svncommunity.digium.com/view/astmanproxy + +For development branches & experimental features: +http://svncommunity.digium.com/view/astmanproxy/branches + +For current development/stable snapshot: +http://svncommunity.digium.com/view/astmanproxy/trunk + +For stable release versions: +http://svncommunity.digium.com/view/astmanproxy/tags + +To download from these repositories: + + - Install Subversion (yum -y install subversion -- or equivalent) + - svn checkout http://svncommunity.digium.com/svn/astmanproxy/trunk + +Be sure to use the full URL path to the version you wish to check out; +for example, do not checkout the 'branches' tree, but instead choose +which branch to checkout, as in: + +http://svncommunity.digium.com/view/astmanproxy/branches/1.2x + +I will also try to post current tarballs here: http://www.popvox.com/astmanproxy -Donations are accepted via paypal to dave@toad.net. +Donations are accepted via paypal to dave@toad.net; beer is also +accepted at Astricon events. :) + +To contact me about contributing to the project, please email: +dave@popvox.com + +I acknowledge all contributions and encourage your experimentation! +AstManProxy would not be where it is without your support!! =================================================================== +AstManProxy Background Information +---------------------------------- + Developing web-based realtime applications for the asterisk open-source PBX often requires interacting with asterisk's Manager interface. The Asterisk Manager runs on port 5038 by default and -- cgit From 23a4a54c8ffbfa1a6430d926bd88f90234a39812 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 8 Apr 2006 02:40:15 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@96 f02b47b9-160a-0410-81a6-dc3441afb0ec --- README | 105 +++++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/README b/README index 4a8b89d..0e8c350 100644 --- a/README +++ b/README @@ -1,10 +1,10 @@ astmanproxy README -(c) 2005-2006 David C. Troy +(c) 2005-2006 David C. Troy, dave@popvox.com ------------------------------------------------------------------ FOREWORD & QUICK START The need for a proxy to Asterisk's manager interface has been -clear; almost all GUI's and other interfaces to asterisk implement a +clear; almost all GUIs and other interfaces to asterisk implement a proxy of some kind. Why? A proxy offers: - A single persistent connection to asterisk @@ -32,7 +32,7 @@ communicate with multiple Asterisk servers. You can use Astmanproxy as the basis for a web-based application: send it data using HTTP POST or HTTP GET, and receive XML output. Or use HTTP POST and get Standard (text/plain) output back! -No web server required! +Astmanproxy speaks HTTP internally, so no web server is required! You can use Astmanproxy as an XML feed for a .NET program that keeps track of Asterisk's state. Or as an interface for injecting quick @@ -46,50 +46,37 @@ To get started quickly, simply: Edit the configuration file: vi /etc/asterisk/astmanproxy.conf +Optionally edit the other config files: + vi /etc/asterisk/astmanproxy.users + vi /etc/asterisk/ssl.conf + Start the program: astmanproxy To view debug output, start astmanproxy in debug mode: astmanproxy -d +For more debug output, add more -d's: + astmanproxy -ddddddd + You may want to start astmanproxy at boot. In that case, you might place it in /etc/rc.d/rc.local: /usr/local/sbin/astmanproxy Please send your feedback! We are looking for contributors to add -support for new I/O formats and to help debug the more complicated -HTTP and XML IO Handlers! +support for new I/O formats and add new features! Contributions: Paypal via dave@toad.net; beer accepted at Astricon events -=================================================================== -INSTALLATION: - - make - make install - -The default configuration file location is -/etc/asterisk/astmanproxy.conf, and that file will be automatically -created upon installation if it does not already exist. - -TO RUN: - -Launch from /etc/rc.d/rc.local or startup script. - -/usr/local/sbin/astmanproxy (or whatever your path is) - -Launch with no options to run as daemon. To debug, use option "-d", -and to display version information, use option "-v". - =================================================================== Additional Proxy Features In addition to exposing the entire Asterisk Manager API as a -pass-through, non-interpreting proxy, 'astmanproxy' can parse client -input where desired; this could be used in the future to add new -features that make sense to be in a proxy but that don't necessarily -need to be in Asterisk proper. +pass-through, non-interpreting proxy, 'astmanproxy' can parse client +input where desired; this could be used in the future to add new +features that should exist in a proxy but that don't +necessarily need to be in Asterisk proper. There are some proxy-specific headers that you can specify in your applications now: @@ -138,6 +125,18 @@ Server: (x.x.x.x|hostname) should exactly match the entry in your config host= section, or whatever name you used with ProxyAction: AddServer. +ProxyKey: secret +Action: Originate +... +ActionID: ... + You can use this as a simple authentication mechanism. + Rather than have to login with a username & password, + you can specify a ProxyKey that must be passed from + a client before requests are processed. This is helpful + in situations where you would like to authenticate and + execute an action in a single step. See the sample + config file for more information. + The proxy also intercepts the following Actions: Action: Login @@ -168,7 +167,7 @@ Blank Commands blank command blocks. =================================================================== -On the Action: Challenge Authentication Mechanism +On the 'Action: Challenge' Authentication Mechanism John Todd wrote this excellent summary of the Action: Challenge Authentication Mechanism, and it accurately describes the @@ -208,6 +207,54 @@ library call, I'm sure): Response: Success Message: Authentication accepted +=================================================================== +On Astmanproxy's SSL Support + +Support for SSL on the Asterisk Manager Interface has recently been +contributed to the Asterisk project (see Digium #6812). + +This SSL implementation has been tested by several people and seems +to work fine. While it is not in a mainline Asterisk distribution +yet (in SVN Trunk only right now), it is likely that AMI will soon +support SSL natively. + +I felt that it was important that Astmanproxy support the same SSL +mechanism as Asterisk; we have been talking about adding SSL/TLS +for some time. So, now it's been incorporated. + +This means you can implement scenarios like: + client <-> proxy <-> n*asterisk +with end-to-end SSL security. + +To make Astmanproxy talk to asterisk, turn on the 'usessl' option +in the server host specification (see astmanproxy.conf). + +To have Astmanproxy talk to clients via SSL, be sure to enable +'allowencryptedconnections' in the astmanproxy.conf file. + +To have Astmanproxy accept ONLY SSL connections, you should +enable 'allowencryptedconnections' and disable +'allowunencryptedconnections'. We've endeavored to use the same +configuration setting names as in manager.conf with the SSL +implementation in #6812. + +=================================================================== +Now Supports HTTPS Natively! + +One really interesting side effect of having both SSL and HTTP support +natively is that we in fact now support HTTPS! + +With the proxy configured on localhost:1234, you can do things +along these lines: + +https://localhost:1234/?Action=ShowChannels&ActionID=Foo + +This has been tested fairly extensively with good results. The +HTTP handler supports both GET and POST and can properly deal +with XML or Standard output formats. With Autofilter=on, +this paradigm is ideal for creating a simple REST-like interface +into Asterisk (even multiple boxes!) with no web servers needed. + =================================================================== Software Updates, Author Info, and How to Contribute -- cgit From e8b7c17417fb7688bb06b2ce825c9838be3f020c Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 8 Apr 2006 02:50:08 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@97 f02b47b9-160a-0410-81a6-dc3441afb0ec --- TODO | 8 ++------ VERSIONS | 1 + configs/astmanproxy.conf | 4 ++-- configs/astmanproxy.users | 8 ++++++++ samples/httpast2.html | 12 ++++++++++++ 5 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 samples/httpast2.html diff --git a/TODO b/TODO index 79ce5b8..a94b30e 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,8 @@ -check s->connect construct -finish SOAP handler +write SOAP methods for http.c to use clean up reconnect to lost asterisk server (socket reuse) clean up debugmsg instances to add if (debug) +deal with http non-conformity better, and act on POST MIME-type inputs -'connected' flag; do not set until we get "authentication accepted' back from server Check for module versions; do not run without modules installed use a key? see loader.c in * State maintenance modules @@ -11,6 +10,3 @@ code formatting tcpwrappers/access control/connect mask? libtool/autoconf/automake support SetInputFormat proxyaction? - -clean up autodisconnect vs. inputcomplete/outputcomplete -deal with http non-conformity better diff --git a/VERSIONS b/VERSIONS index 9559284..72c7d89 100644 --- a/VERSIONS +++ b/VERSIONS @@ -8,6 +8,7 @@ Added SSL Support (Remco Treffkorn, Mahesh Karoshi, John Todd; Tello Corp) Added 'usessl' option for connecting to asterisk servers Fixed proxykey pointer bug (Steve Davies) + Ditched autodisconnect handler property in favor of inputcomplete/outputcomplete ------------------------------------------------------------------------------------------------ 1.1pre2 Completely Modularized and Abstracted Input/Output formats diff --git a/configs/astmanproxy.conf b/configs/astmanproxy.conf index 6164e9c..6d46bb2 100644 --- a/configs/astmanproxy.conf +++ b/configs/astmanproxy.conf @@ -5,8 +5,8 @@ ; List of asterisk host(s) you want to proxy ; host = ip_addr, port, user, secret, events, use_ssl host = localhost, 5038, dave, moo, on, off -host = 192.168.1.173, 5038, dave, moo, on, on +;host = 192.168.1.173, 5038, dave, moo, on, on ;host = 127.0.0.2, 5038, user, secret, on ;host = otherhost, 5038, user, secret, on ;host = newhost, 5030, user, secret, off @@ -28,7 +28,7 @@ sslclienthellotimeout = 200 acceptencryptedconnection = yes ; Do we accept unencrypted manager connections? -acceptunencryptedconnection = no +acceptunencryptedconnection = yes ; Amount of time to wait before timing out on writes to asterisk asteriskwritetimeout=100 diff --git a/configs/astmanproxy.users b/configs/astmanproxy.users index 2645fc9..9e31056 100644 --- a/configs/astmanproxy.users +++ b/configs/astmanproxy.users @@ -1,2 +1,10 @@ +; Astmanproxy user list +; +; Reload permissions by sending a SIGHUP +; +; "user" is the username, secret is the password, and the (optional) +; channel setting causes filtering of events only for the specified +; channel to be sent to this user. +; ; user=secret,channel,out_context (to Asterisk),in_context (From Asterisk) steve=steve,SIP/snom190,local, diff --git a/samples/httpast2.html b/samples/httpast2.html new file mode 100644 index 0000000..66baa2e --- /dev/null +++ b/samples/httpast2.html @@ -0,0 +1,12 @@ + +

Sample Astmanproxy HTTP Input

+This version uses the GET method with an HTTPS action. Be sure +you have acceptencryptedconnections=yes in astmnanproxy.conf. +

+

+Server:
+Action:
+ActionID:
+
+
+ -- cgit From edf54a3c22f04b10a0ff8694cf03ed81fd7079c0 Mon Sep 17 00:00:00 2001 From: David Troy Date: Sat, 8 Apr 2006 02:54:01 +0000 Subject: git-svn-id: http://svncommunity.digium.com/svn/astmanproxy/branches/1.20pre@98 f02b47b9-160a-0410-81a6-dc3441afb0ec --- configs/astmanproxy.conf | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/configs/astmanproxy.conf b/configs/astmanproxy.conf index 6d46bb2..be28edf 100644 --- a/configs/astmanproxy.conf +++ b/configs/astmanproxy.conf @@ -1,6 +1,6 @@ ; astmanproxy.conf ; Asterisk Manager Proxy Configuration Sample -; (C) 2005-2006 David C. Troy +; (C) 2005-2006 David C. Troy - dave@popvox.com ; List of asterisk host(s) you want to proxy ; host = ip_addr, port, user, secret, events, use_ssl @@ -48,6 +48,10 @@ listenaddress = * ; Port for proxy to listen on listenport = 1234 +; Do we require authentication (either proxykey or astmanproxy.users entry)? +; See README and astmanproxy.users for more info +authrequired = no + ; Setting a proxy key requires proxy client connections to ; specify a ProxyKey: keyvalue header in the first incoming request ; to the proxy. Once this is done the client remains authenticated. @@ -57,10 +61,7 @@ listenport = 1234 ; in place and well understood. ; proxykey = foobar -; Do we require authentication (either proxykey or astmanproxy.users entry)? -authrequired = no - -; user and group for proxy to run as; will NOT run as root! +; local user and group for proxy to run as; will NOT run as root! proc_user = nobody proc_group = nobody @@ -70,6 +71,15 @@ proc_group = nobody inputformat = standard outputformat = standard +; to enable REST/XMLRPC-like functionality, try this combo. +; this gives you http input (POST or GET) and either +; text/xml or text/plain output with NO webserver required! +; to access: http://[host]:1234/?Action=Ping&ActionID=Foo +; +; inputformat = http +; outputfomat = xml|standard +; autofilter = on + ; set autofilter to be on or off by default ; with autofilter on, you can automatically filter responses ; to include only messages related to a specific actionid, -- cgit