summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Troy <dave@popvox.com>2008-06-23 11:37:23 -0400
committerDavid Troy <dave@popvox.com>2008-06-23 11:37:23 -0400
commitf722ade2a59872cb8c62e81809ef4d83d11434e5 (patch)
tree350ed9dda94796a3710c68a52e3690942cb73140
downloadastmanproxy-f722ade2a59872cb8c62e81809ef4d83d11434e5.tar.gz
astmanproxy-f722ade2a59872cb8c62e81809ef4d83d11434e5.tar.xz
astmanproxy-f722ade2a59872cb8c62e81809ef4d83d11434e5.zip
added files to project
-rw-r--r--INSTALL27
-rw-r--r--Makefile176
-rw-r--r--README384
-rw-r--r--TODO11
-rw-r--r--VERSIONS67
-rw-r--r--configs/astmanproxy.conf97
-rw-r--r--configs/astmanproxy.users10
-rw-r--r--configs/ssl.conf154
-rw-r--r--debian/README.Debian6
-rw-r--r--debian/astmanproxy-default.ex10
-rw-r--r--debian/astmanproxy.155
-rw-r--r--debian/astmanproxy.doc-base.EX22
-rw-r--r--debian/changelog6
-rw-r--r--debian/compat1
-rw-r--r--debian/conffiles1
-rw-r--r--debian/control12
-rw-r--r--debian/copyright10
-rw-r--r--debian/dirs2
-rw-r--r--debian/docs2
-rw-r--r--debian/init.d.ex74
-rw-r--r--debian/postinst.ex42
-rw-r--r--debian/postrm.ex38
-rw-r--r--debian/preinst.ex38
-rw-r--r--debian/prerm.ex38
-rw-r--r--debian/rules98
-rw-r--r--debian/watch18
-rw-r--r--doc/README.csv7
-rw-r--r--doc/README.http24
-rw-r--r--doc/README.standard15
-rw-r--r--doc/README.xml21
-rw-r--r--samples/httpast.html13
-rw-r--r--samples/httpast2.html12
-rw-r--r--src/astmanproxy.c720
-rw-r--r--src/common.c146
-rw-r--r--src/config.c313
-rw-r--r--src/config_perms.c135
-rw-r--r--src/csv.c31
-rw-r--r--src/dlfcn.c1282
-rw-r--r--src/http.c238
-rw-r--r--src/include/astmanproxy.h148
-rw-r--r--src/include/dlfcn-compat.h83
-rw-r--r--src/include/endian.h60
-rw-r--r--src/include/md5.h18
-rw-r--r--src/include/poll-compat.h101
-rw-r--r--src/include/ssl.h89
-rw-r--r--src/log.c67
-rw-r--r--src/md5.c260
-rw-r--r--src/poll.c306
-rw-r--r--src/proxyfunc.c398
-rw-r--r--src/ssl.c422
-rw-r--r--src/standard.c70
-rw-r--r--src/xml.c159
52 files changed, 6537 insertions, 0 deletions
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..1842a67
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,27 @@
+Quick Install Instructions
+--------------------------
+
+Requirements: openssl, openssl-devel
+
+Installation is easy:
+
+1. make
+2. make install
+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/Makefile b/Makefile
new file mode 100644
index 0000000..7627d50
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,176 @@
+.EXPORT_ALL_VARIABLES:
+
+OSARCH=$(shell uname -s)
+OSREV=$(shell uname -r)
+
+VERSION := 1.22pre
+DESTDIR ?=
+CONFDIR:=/etc/asterisk
+CONFDIR_REAL := $(DESTDIR)/etc/asterisk
+PERMDIR:=/etc/asterisk
+PERMDIR_REAL := $(DESTDIR)/etc/asterisk
+DISTDIR:=/var/www/html/astmanproxy
+
+LIBDIR := $(DESTDIR)/usr/lib/astmanproxy
+CONFFILE := astmanproxy.conf
+PERMFILE := astmanproxy.users
+
+CC := gcc
+INCLUDES :=
+PREFIX:= /usr/local
+BINDIR := $(DESTDIR)$(PREFIX)/sbin
+
+# For compilation dependencies
+MODS := astmanproxy config config_perms common proxyfunc log ssl md5
+HANDLERS := xml standard csv http
+SOBJS := $(HANDLERS:%=%.so)
+LIBS := -lssl
+
+# Add -g below for debug/GDB symbols
+CFLAGS:=-Wall -O2 -D_REENTRANT -fPIC -Isrc/include -I/usr/include/openssl
+
+ifeq (${OSARCH},Darwin)
+ LIBS+=-lresolv
+ CFLAGS+=-D__Darwin_ -Iquotesrc/include
+ 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
+ CERTDIR := /opt/lib/asterisk/certs
+ ifeq (${OSREV},7.9.0)
+ OBJS+=poll.o dlfcn.o
+ endif
+ ASTLINK=-Wl,-force_flat_namespace,-dynamic
+ SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace
+ MKTEMP=/usr/bin/mktemp
+else
+ #These are used for all but Darwin
+ CFLAGS+=-I-
+ LIBS+=-ldl -pthread
+ ASTLINK=-Wl,-E
+ SOLINK=-shared -Xlinker -x
+ LOGDIR=/var/log/asterisk
+ CERTDIR := /var/lib/asterisk/certs
+ MKTEMP=/bin/mktemp
+endif
+
+MODDIR := $(LIBDIR)/modules
+DEFINES:='-DPROXY_VERSION="$(VERSION)"' '-DCDIR="$(CONFDIR)"' '-DCFILE="$(CONFFILE)"'
+DEFINES+='-DMDIR="$(MODDIR)"' '-DPDIR="$(PERMDIR)"' '-DPFILE="$(PERMFILE)"'
+
+PROXYCERT := $(CERTDIR)/proxy-server.pem
+PROXYSSLCONF := $(CONFDIR)/proxy-ssl.conf
+
+CFLAGS += $(DEFINES)
+
+OBJS += $(MODS:%=%.o)
+CONF_TARGET:= $(CONFDIR_REAL)/$(CONFFILE)
+PERM_TARGET:= $(PERMDIR_REAL)/$(PERMFILE)
+VPATH = src
+
+
+# For printing only
+SRCS := $(MODS:%=src/%.c)
+HDRS := src/include/astmanproxy.h
+
+all: astmanproxy cert
+
+astmanproxy: $(OBJS) $(SOBJS)
+ $(CC) $(CFLAGS) -o $@ $(ASTLINK) $(OBJS) $(LIBS)
+
+$(OBJS): %.o: %.c
+ $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+
+$(SOBJS): %.so: %.o
+ $(CC) $(SOLINK) $< -o $@
+
+SERIAL=`date "+%Y%m%d%H%M%S"`
+
+cert:
+ if [ ! -f $(PROXYCERT) ]; then \
+ umask 77 ; \
+ PEM1=`$(MKTEMP) /tmp/openssl.XXXXXX` ; \
+ PEM2=`$(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=`$(MKTEMP) /tmp/openssl.XXXXXX` ; \
+ PEM2=`$(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
+
+
+install: uninstall all
+ install -d $(BINDIR)
+ install astmanproxy $(BINDIR)
+ install -d $(LIBDIR)
+ install -d $(MODDIR)
+ install $(SOBJS) $(MODDIR)
+ install -d $(CONFDIR_REAL)
+ if [ ! -f $(CONF_TARGET) ]; then \
+ install ./configs/$(CONFFILE) $(CONF_TARGET); \
+ fi
+ if [ ! -f $(PERM_TARGET) ]; then \
+ install ./configs/$(PERMFILE) $(PERM_TARGET); \
+ fi
+ @echo "Installation Complete!"
+
+uninstall:
+ rm -f $(BINDIR)/astmanproxy
+ cd $(MODDIR); rm -f $(SOBJS)
+ @echo "Successfully uninstalled!"
+
+dist: clean
+ rm -rf /tmp/astmanproxy-${VERSION}*; \
+ cp -R . /tmp/astmanproxy-${VERSION}; \
+ cd /tmp; tar czf /tmp/astmanproxy-${VERSION}-`date +%Y%m%d-%H%M`.tgz astmanproxy-${VERSION}; \
+ /usr/bin/scp /tmp/astmanproxy-${VERSION}-*.tgz root@www.popvox.com:$(DISTDIR); \
+ /usr/bin/ssh -lroot www.popvox.com "ln -sf $(DISTDIR)/astmanproxy-${VERSION}-*.tgz $(DISTDIR)/astmanproxy-latest.tgz"
+
+clean:
+ rm -f *.o *.so core *~ astmanproxy proxy-server.pem;
+
+print:
+ more Makefile $(HDRS) $(SRCS) | enscript -Ec -2r -j; exit 0
+ @echo "Printing Complete!"
+
+love:
+ @echo "Here? Now?"
+
diff --git a/README b/README
new file mode 100644
index 0000000..424e628
--- /dev/null
+++ b/README
@@ -0,0 +1,384 @@
+astmanproxy README
+(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 GUIs and other interfaces to asterisk implement a
+proxy of some kind. Why? A proxy offers:
+
+ - A single persistent connection to asterisk
+ - A more secure (non-root) TCP interface
+ - Ability to offer filtered input/output
+ - Less connections and networking load for asterisk
+
+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 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
+ - Written in c/pthreads to be fast and robust
+
+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!
+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
+commands into multiple Asterisk boxes from your Python scripts. The
+possibilities are limited only by your imagination.
+
+To get started quickly, simply:
+ make
+ make install
+
+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 add new features!
+
+Contributions:
+ Paypal via dave@toad.net; beer accepted at Astricon events
+
+===================================================================
+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 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:
+
+Server: (x.x.x.x|hostname)
+ Specify a server to which to send your commands. This should match
+ the server name specified in your config file's "host" entry.
+ If you do not specify a server, the proxy will pick the first one
+ it finds -- fine in single-server configurations.
+
+Some "ProxyActions" have been implemented which the Proxy responds to
+but does not pass on to Asterisk itself:
+
+ProxyAction: ListSessions
+ Outputs a list of all active client and server sessions
+
+ProxyAction: SetOutputFormat
+OutputFormat: (standard|xml)
+ Sets the output format on a per-client basis
+
+ProxyAction: SetAutoFilter
+AutoFilter: (on|off)
+ Sets the AutoFilter property on a per-client basis
+ (See autofiltering section below)
+
+ProxyAction: Logoff
+ Logs client out of Proxy and leaves Asterisk alone.
+
+ProxyAction: ListIOHandlers
+ Lists all available Input/Output Handlers
+ Examples include Standard, XML, and CSV; more I/O
+ formats may be added by creating new handler modules.
+
+ProxyAction: AddServer
+Server: (x.x.x.x|hostname)
+Port: (5038|other)
+Username: (username)
+Secret: (secret)
+Events: (on|off)
+ Initiates a proxy connection to a new Asterisk Server; this
+ has the same effect of including a host entry in your
+ host= section of the configuration file.
+
+ProxyAction: DropServer
+Server: (x.x.x.x|hostname)
+ Disconnects a proxy<->server session. Hostname specified
+ 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
+ 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
+ Asterisk. The proxy intercepts "Action: Logoff" and interprets
+ it as "ProxyAction: Logoff". This keeps the proxy from
+ disconnecting from Asterisk.
+
+Blank Commands
+ The proxy does not send commands to Asterisk until you have
+ a fully formed Action block. This keeps unnecessary traffic
+ and load off of Asterisk. The proxy intercepts and ignores
+ blank command blocks.
+
+===================================================================
+AstManProxy Autofiltering Functionality
+
+One of the most powerful features of AstManProxy is its ability to
+automatically filter output on a per-client basis. It can do this
+with its Autofilter capability, which can be set 'on' in the config
+file or enabled via the ProxyAction: SetAutoFilter function.
+
+With autofiltering enabled, each client only receives output containing
+the "ActionID" parameter it has set most recently. This is useful
+for single atomic requests into asterisk from a client, such as
+when creating a simple UI to inject a command.
+
+For example, if a client sends this packet while autofiltering is
+enabled:
+
+Action: Ping
+ActionID: foo
+
+Then the autofilter ActionID for that client is set to foo, and no
+output besides for responses containing "foo" will be returned
+to that client, such as:
+
+Response: Pong
+ActionID: foo
+
+Replace Ping with Originate and Pong with Success and you can see
+how this same mechanism can be used to quickly query asterisk
+box(es), initiate calls, etc, without your client having to worry
+with filtering a lot of unrelated output.
+
+===================================================================
+On the astmanproxy.users output filtering functionality
+
+Users may now be defined in your astmanproxy.users configuration file.
+This enables a traditional user/password based login mechanism
+for Astmanproxy similar to what is found in Asterisk. Output may be
+filtered on a per-user basis.
+
+"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,
+
+===================================================================
+On the 'Action: Challenge' Authentication Mechanism
+
+John Todd wrote this excellent summary of the Action: Challenge
+Authentication Mechanism, and it accurately describes the
+implementation included in astmanproxy:
+
+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
+
+===================================================================
+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
+
+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/svn/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; 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
+provides a simple TCP interface into several common asterisk
+functions, including call origination, redirection, hangup, and many
+other functions.
+
+Each interaction from a web-based application requires a separate
+connection to the manager interface. Asterisk, being a real time
+application, shouldn't have to deal with the load of constant
+connections, disconnections, and authentications from a single
+trusted client, namely your web app.
+
+In the same way that web developers have solved this problem for
+other similar services (imapproxy for IMAP web mail clients,
+database connection caches, etc), 'astmanproxy' sets out to solve
+this problem for asterisk.
+
+This project started out as a simple proof-of-concept script called
+"simpleproxy.pl" which was made available in September 2004,
+following a discussion at the Astricon conference regarding the need
+for such a proxy. That code was based on Nicolas Gudino's manager
+proxy for his excellent Flash Operator Panel. Written in perl and
+as a single-threaded select-based "dumb" proxy, simpleproxy.pl has
+been widely used as a basis for experimentation, but I wanted
+something more robust and that could act as a basis for additional
+features.
+
+Asterisk Manager Proxy is a multithreaded proxy server for Asterisk
+Manager written in c, and based on several of the same data
+structures and routines found in the asterisk manager itself. This
+insures a very high degree of compatibility with Asterisk; it should
+also be very robust and scalable.
+
+Asterisk Manager Proxy gives the ability to support multiple input
+and output format -- implemented as abstracted I/O handlers -- and
+these are configurable on a per-client basis.
+
+===================================================================
+(C) 2005-2006 David C. Troy, dave@popvox.com
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..839aa55
--- /dev/null
+++ b/TODO
@@ -0,0 +1,11 @@
+trap action=command in xml.c; remove arbitrary detection of unparsed data
+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
+
+Check for module versions; do not run without modules installed
+State maintenance modules
+tcpwrappers/access control/connect mask?
+libtool/autoconf/automake support
+SetInputFormat proxyaction?
diff --git a/VERSIONS b/VERSIONS
new file mode 100644
index 0000000..05825d7
--- /dev/null
+++ b/VERSIONS
@@ -0,0 +1,67 @@
+1.21 Major code formatting cleanup and official release of 1.21pre (trunk)
+ Documentation overhaul & cleanup
+
+1.21pre Added URLDecode routine to http.c to deal with URL-encoded GET/POST data
+ Fixed xml.c to better deal with cli/unparsed data
+ Cleaned up Makefile for better support of Mac OS X 10.4
+ Changed message to be dynamically allocated in HandleAsterisk; solved bus error on Mac OS X 10.4
+
+1.20 Cleanup and official release of 1.20pre
+
+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)
+ 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
+ Fixed proxykey pointer bug (Steve Davies)
+ Ditched autodisconnect handler property in favor of inputcomplete/outputcomplete
+
+------------------------------------------------------------------------------------------------
+1.1pre2 Completely Modularized and Abstracted Input/Output formats
+ Cleaned up session write mutex code (Peter Loeppky)
+ Fixed message initialization bug -- using wrong sizeof
+ Added XML Input format
+ Added HTTP Input format
+ Added CSV Output format
+ Support for connections to an arbitrary number of Asterisk servers
+ Added retryinterval and maxretries to conf for reconnecting lost servers
+
+1.1pre3 Added _autodisconnect property for I/O handlers
+ Added _onconnect method for I/O handlers
+ Returned connection banner to standard input handler via _onconnect method
+
+1.1 Finalized /usr/lib/astmanproxy/modules for shared objects in Makefile
+ Added make uninstall target
+ Added fail on no I/O handlers loaded
+ Updated README, added samples dir
+1.11 Added ProxyAction ListServers (Richard Lyman)
+ Fixed output bug, excess terminators (Dennis Persson)
+1.12 Added support for bad/old-style Asterisk output (Ron Arts)
+ Tweaked for compatibility with Flash Operator Panel (Nicolas Gudino)
+1.13 Changed to read only /etc/asterisk/astmanproxy.conf and not ./astmanproxy.conf
+ Added a 'connected' field so we don't try to write to servers which are not yet connected
+ Exits when there are no servers able to connect
+ Doesn't attempt to re-connect to a server if we get 'Authentication failed'
+ Added a connection timeout by using connect_nonb
+ Confirmed support for x86-64 processors (added -fPIC; Jennifer Hales)
+ 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)
+
+------------------------------------------------------------------------------------------------
+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)
diff --git a/configs/astmanproxy.conf b/configs/astmanproxy.conf
new file mode 100644
index 0000000..5c2f480
--- /dev/null
+++ b/configs/astmanproxy.conf
@@ -0,0 +1,97 @@
+; astmanproxy.conf
+; Asterisk Manager Proxy Configuration Sample
+; (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
+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 = yes
+
+; 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
+
+; 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.
+; 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
+
+; local 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
+
+; 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,
+; 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
+; /opt/log location is good on Mac OS X
+;logfile = /opt/log/asterisk/astmanproxy.log
+logfile = /var/log/asterisk/astmanproxy.log
+
diff --git a/configs/astmanproxy.users b/configs/astmanproxy.users
new file mode 100644
index 0000000..9e31056
--- /dev/null
+++ b/configs/astmanproxy.users
@@ -0,0 +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/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/debian/README.Debian b/debian/README.Debian
new file mode 100644
index 0000000..a5d7f45
--- /dev/null
+++ b/debian/README.Debian
@@ -0,0 +1,6 @@
+astmanproxy for Debian
+----------------------
+
+<possible notes regarding this package - if none, delete this file>
+
+ -- Tzafrir Cohen <tzafrir.cohen@xorcom.com>, Tue, 7 Jun 2005 20:27:34 +0300
diff --git a/debian/astmanproxy-default.ex b/debian/astmanproxy-default.ex
new file mode 100644
index 0000000..6c539de
--- /dev/null
+++ b/debian/astmanproxy-default.ex
@@ -0,0 +1,10 @@
+# Defaults for astmanproxy initscript
+# sourced by /etc/init.d/astmanproxy
+# installed at /etc/default/astmanproxy by the maintainer scripts
+
+#
+# This is a POSIX shell fragment
+#
+
+# Additional options that are passed to the Daemon.
+DAEMON_OPTS=""
diff --git a/debian/astmanproxy.1 b/debian/astmanproxy.1
new file mode 100644
index 0000000..b5a47d8
--- /dev/null
+++ b/debian/astmanproxy.1
@@ -0,0 +1,55 @@
+.\" Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH ASTMANPROXY 1 "June 7, 2005"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh disable hyphenation
+.\" .hy enable hyphenation
+.\" .ad l left justify
+.\" .ad b justify to both left and right margins
+.\" .nf disable filling
+.\" .fi enable filling
+.\" .br insert line break
+.\" .sp <n> insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+astmanproxy \- An asterisk manager interface proxy
+.SH SYNOPSIS
+.B astmanproxy
+.RI [ -d ]
+
+.B astmanproxy
+.RI -h
+
+.SH DESCRIPTION
+.B astmanproxy
+is a proxy between the manager interface of
+.B asterisk(8)
+and other clients.
+
+The program may do some \fBinteresting\fR \fIthings\fR, but someone else
+needs to write its man page (hint: very simple)
+
+.SH OPTIONS
+.B -d
+.RS
+Debug mode
+.RE
+
+.B -h
+.RS
+Display help message and exit.
+.RE
+
+.SH SEE ALSO
+.BR asterisk (8)
+
+any reference page?
+
+.SH AUTHOR
+astmanproxy was written by David C. Troy <dave@popvox.com>.
+
+This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
diff --git a/debian/astmanproxy.doc-base.EX b/debian/astmanproxy.doc-base.EX
new file mode 100644
index 0000000..279381e
--- /dev/null
+++ b/debian/astmanproxy.doc-base.EX
@@ -0,0 +1,22 @@
+Document: astmanproxy
+Title: Debian astmanproxy Manual
+Author: <insert document author here>
+Abstract: This manual describes what astmanproxy is
+ and how it can be used to
+ manage online manuals on Debian systems.
+Section: unknown
+
+Format: debiandoc-sgml
+Files: /usr/share/doc/astmanproxy/astmanproxy.sgml.gz
+
+Format: postscript
+Files: /usr/share/doc/astmanproxy/astmanproxy.ps.gz
+
+Format: text
+Files: /usr/share/doc/astmanproxy/astmanproxy.text.gz
+
+Format: HTML
+Index: /usr/share/doc/astmanproxy/html/index.html
+Files: /usr/share/doc/astmanproxy/html/*.html
+
+
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..e8deb96
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,6 @@
+astmanproxy (1.0-1) unstable; urgency=low
+
+ * Initial Release.
+
+ -- Tzafrir Cohen <tzafrir.cohen@xorcom.com> Tue, 7 Jun 2005 20:27:34 +0300
+
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..b8626c4
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+4
diff --git a/debian/conffiles b/debian/conffiles
new file mode 100644
index 0000000..17d440b
--- /dev/null
+++ b/debian/conffiles
@@ -0,0 +1 @@
+/etc/asterisk/astmanproxy.conf
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..af4c21e
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,12 @@
+Source: astmanproxy
+Section: unknown
+Priority: optional
+Maintainer: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
+Build-Depends: debhelper (>= 4.0.0)
+Standards-Version: 3.6.1
+
+Package: astmanproxy
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: <insert up to 60 chars description>
+ <insert long description, indented with spaces>
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..9f53581
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,10 @@
+This package was debianized by Tzafrir Cohen <tzafrir.cohen@xorcom.com> on
+Tue, 7 Jun 2005 20:27:34 +0300.
+
+It was downloaded from <fill in ftp site>
+
+Copyright Holder: <put author(s) name and email here>
+
+License:
+
+<Put the license of the package here>
diff --git a/debian/dirs b/debian/dirs
new file mode 100644
index 0000000..ca882bb
--- /dev/null
+++ b/debian/dirs
@@ -0,0 +1,2 @@
+usr/bin
+usr/sbin
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..4242a3d
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1,2 @@
+README
+VERSIONS
diff --git a/debian/init.d.ex b/debian/init.d.ex
new file mode 100644
index 0000000..b1155e8
--- /dev/null
+++ b/debian/init.d.ex
@@ -0,0 +1,74 @@
+#! /bin/sh
+#
+# skeleton example file to build /etc/init.d/ scripts.
+# This file should be used to construct scripts for /etc/init.d.
+#
+# Written by Miquel van Smoorenburg <miquels@cistron.nl>.
+# Modified for Debian
+# by Ian Murdock <imurdock@gnu.ai.mit.edu>.
+#
+# Version: @(#)skeleton 1.9 26-Feb-2001 miquels@cistron.nl
+#
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/usr/sbin/astmanproxy
+NAME=astmanproxy
+DESC=astmanproxy
+
+test -x $DAEMON || exit 0
+
+# Include astmanproxy defaults if available
+if [ -f /etc/default/astmanproxy ] ; then
+ . /etc/default/astmanproxy
+fi
+
+set -e
+
+case "$1" in
+ start)
+ echo -n "Starting $DESC: "
+ start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
+ --exec $DAEMON -- $DAEMON_OPTS
+ echo "$NAME."
+ ;;
+ stop)
+ echo -n "Stopping $DESC: "
+ start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \
+ --exec $DAEMON
+ echo "$NAME."
+ ;;
+ #reload)
+ #
+ # If the daemon can reload its config files on the fly
+ # for example by sending it SIGHUP, do it here.
+ #
+ # If the daemon responds to changes in its config file
+ # directly anyway, make this a do-nothing entry.
+ #
+ # echo "Reloading $DESC configuration files."
+ # start-stop-daemon --stop --signal 1 --quiet --pidfile \
+ # /var/run/$NAME.pid --exec $DAEMON
+ #;;
+ restart|force-reload)
+ #
+ # If the "reload" option is implemented, move the "force-reload"
+ # option to the "reload" entry above. If not, "force-reload" is
+ # just the same as "restart".
+ #
+ echo -n "Restarting $DESC: "
+ start-stop-daemon --stop --quiet --pidfile \
+ /var/run/$NAME.pid --exec $DAEMON
+ sleep 1
+ start-stop-daemon --start --quiet --pidfile \
+ /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS
+ echo "$NAME."
+ ;;
+ *)
+ N=/etc/init.d/$NAME
+ # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
+ echo "Usage: $N {start|stop|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/debian/postinst.ex b/debian/postinst.ex
new file mode 100644
index 0000000..cb971f4
--- /dev/null
+++ b/debian/postinst.ex
@@ -0,0 +1,42 @@
+#! /bin/sh
+# postinst script for astmanproxy
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+#
+
+case "$1" in
+ configure)
+
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/postrm.ex b/debian/postrm.ex
new file mode 100644
index 0000000..8d38558
--- /dev/null
+++ b/debian/postrm.ex
@@ -0,0 +1,38 @@
+#! /bin/sh
+# postrm script for astmanproxy
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+
+
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/preinst.ex b/debian/preinst.ex
new file mode 100644
index 0000000..8f49b09
--- /dev/null
+++ b/debian/preinst.ex
@@ -0,0 +1,38 @@
+#! /bin/sh
+# preinst script for astmanproxy
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <new-preinst> `install'
+# * <new-preinst> `install' <old-version>
+# * <new-preinst> `upgrade' <old-version>
+# * <old-preinst> `abort-upgrade' <new-version>
+#
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ install|upgrade)
+ ;;
+
+ abort-upgrade)
+ ;;
+
+ *)
+ echo "preinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/prerm.ex b/debian/prerm.ex
new file mode 100644
index 0000000..61aa598
--- /dev/null
+++ b/debian/prerm.ex
@@ -0,0 +1,38 @@
+#! /bin/sh
+# prerm script for astmanproxy
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <prerm> `remove'
+# * <old-prerm> `upgrade' <new-version>
+# * <new-prerm> `failed-upgrade' <old-version>
+# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
+# * <deconfigured's-prerm> `deconfigure' `in-favour'
+# <package-being-installed> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ remove|upgrade|deconfigure)
+ ;;
+ failed-upgrade)
+ ;;
+ *)
+ echo "prerm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/rules b/debian/rules
new file mode 100644
index 0000000..b791344
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,98 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+MAKE += PREFIX=/usr
+
+
+CFLAGS = -Wall -g
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+ CFLAGS += -O0
+else
+ CFLAGS += -O2
+endif
+
+configure: configure-stamp
+configure-stamp:
+ dh_testdir
+ # Add here commands to configure the package.
+
+ touch configure-stamp
+
+
+build: build-stamp
+
+build-stamp: configure-stamp
+ dh_testdir
+
+ # Add here commands to compile the package.
+ $(MAKE)
+ #docbook-to-man debian/astmanproxy.sgml > astmanproxy.1
+
+ touch build-stamp
+
+clean:
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp configure-stamp
+
+ # Add here commands to clean up after the build process.
+ -$(MAKE) clean
+
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+ # Add here commands to install the package into debian/astmanproxy.
+ $(MAKE) install DESTDIR=$(CURDIR)/debian/astmanproxy
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs
+ dh_installdocs
+ dh_installexamples astmanproxy.conf
+# dh_install
+# dh_installmenu
+# dh_installdebconf
+# dh_installlogrotate
+# dh_installemacsen
+# dh_installpam
+# dh_installmime
+# dh_installinit
+# dh_installcron
+# dh_installinfo
+ dh_installman debian/astmanproxy.1
+ dh_link
+ dh_strip
+ dh_compress
+ dh_fixperms
+# dh_perl
+# dh_python
+# dh_makeshlibs
+ dh_installdeb
+ dh_shlibdeps
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..07dfb37
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,18 @@
+# Example watch control file for uscan
+# Rename this file to "watch" and then you can run the "uscan" command
+# to check for upstream updates and more.
+# See uscan(1) for format
+
+# Compulsory line, this is a version 2 file
+version=2
+
+http://www.popvox.com/astmanproxy/ astmanproxy-([0-9]*[0-9a-zA-Z.-]*).tgz
+# Uncomment to examine a Webpage
+# <Webpage URL> <string match>
+#http://www.example.com/downloads.php astmanproxy-(.*)\.tar\.gz
+
+# Uncomment to examine a Webserver directory
+#http://www.example.com/pub/astmanproxy-(.*)\.tar\.gz
+
+# Uncommment to examine a FTP server
+#ftp://ftp.example.com/pub/astmanproxy-(.*)\.tar\.gz debian uupdate
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:
+
+<HTML>
+<FORM ACTION="http://myhost:1234" METHOD=POST>
+Action: <input name="Action" type="text" value="Ping"><br>
+ActionID: <input name="ActionID" type="text" value="Foo"><br>
+<input type=submit><br>
+</FORM>
+</HTML>
+
+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:
+
+<AsteriskManagerInput>
+ <Action Value="Ping"/>
+ <ActionID Value="foo"/>
+</AsteriskManagerInput>
+
+<AsteriskManagerInput>
+ <ProxyAction Value="SetOutputFormat"/>
+ <Outputformat Value="CSV"/>
+</AsteriskManagerInput>
+
+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/samples/httpast.html b/samples/httpast.html
new file mode 100644
index 0000000..bfd97fa
--- /dev/null
+++ b/samples/httpast.html
@@ -0,0 +1,13 @@
+<HTML>
+<h3>Sample Astmanproxy HTTP Input</h3>
+Use form name/value pairs with GET or POST method to astmanproxy's<br>
+HTTP input handler and XML output handler to get an instant<br>
+Web interface to Asterisk!
+<p>
+<FORM ACTION="http://localhost:1234" METHOD=POST>
+Server: <input type=text name=Server value="127.0.0.1"><br>
+Action: <input name="Action" type="text" value="Ping"><br>
+ActionID: <input name="ActionID" type="text" value="Foo"><br>
+<input type=submit><br>
+</FORM>
+</HTML>
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 @@
+<HTML>
+<h3>Sample Astmanproxy HTTP Input</h3>
+This version uses the GET method with an HTTPS action. Be sure
+you have acceptencryptedconnections=yes in astmnanproxy.conf.
+<p>
+<FORM ACTION="https://localhost:1234" METHOD=GET>
+Server: <input type=text name=Server value="127.0.0.1"><br>
+Action: <input name="Action" type="text" value="Ping"><br>
+ActionID: <input name="ActionID" type="text" value="Foo"><br>
+<input type=submit><br>
+</FORM>
+</HTML>
diff --git a/src/astmanproxy.c b/src/astmanproxy.c
new file mode 100644
index 0000000..b2a7107
--- /dev/null
+++ b/src/astmanproxy.c
@@ -0,0 +1,720 @@
+/* Asterisk Manager Proxy
+ Copyright (c) 2005-2006 David C. Troy <dave@popvox.com>
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License.
+
+ astmanproxy.c
+ contains the proxy server core, initialization, thread launching,
+ loops, and exit routines
+*/
+
+#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, struct message *m);
+extern void *ProxyLogoff(struct mansession *s);
+extern int ValidateAction(struct message *m, struct mansession *s, int inbound);
+
+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_sock(c->fd); /* close tcp & ssl socket */
+ 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_sock(asock); /* close tcp & ssl socket */
+
+ /* 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 2005-2006\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));
+ close_sock(s->fd); /* close tcp/ssl socket */
+ pthread_mutex_destroy(&s->lock);
+ free(s);
+ } else if (debug)
+ 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->inputcomplete) {
+ pthread_mutex_lock(&c->lock);
+ c->outputcomplete = 1;
+ pthread_mutex_unlock(&c->lock);
+ }
+ }
+ 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; i<m->hdrcount; i++) {
+ if (strcasecmp(m->headers[i], "Server:") ) {
+ sprintf(outstring, "%s\r\n", m->headers[i]);
+ ast_carefulwrite(s->fd, outstring, strlen(outstring), s->writetimeout );
+ }
+ }
+ ast_carefulwrite(s->fd, "\r\n", 2, s->writetimeout);
+ 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) );
+ 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) {
+ /* Check for anything that requires proxy-side processing */
+ if (pc.key[0] != '\0' && !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") )
+ 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) ) {
+ if ( !(*actionid == '\0') )
+ setactionid(actionid, &m, s);
+ if ( !WriteAsterisk(&m) )
+ break;
+ } else {
+ SendError(s, "Action Filtered");
+ }
+ } 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;
+ if (! (m = malloc(sizeof(struct message))) )
+ goto leave;
+
+ for (;;) {
+ if (debug)
+ 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; i<m->hdrcount; i++) {
+ debugmsg("asterisk@%s got: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.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;
+ } else if (res < 0) {
+ /* 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;
+ }
+ }
+ free(m);
+
+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, 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) );
+ 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 ( ast_connect(s) == -1 ) {
+ if (debug)
+ 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;
+ } 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;
+ }
+
+ /* set default handlers if non match was found */
+ if (!s->output) {
+ s->output = iohandlers;
+ res = 1;
+ }
+
+ if (!s->input) {
+ s->input = iohandlers;
+ res = 1;
+ }
+ pthread_mutex_unlock(&s->lock);
+
+ return res;
+}
+
+static void *accept_thread()
+{
+ int as;
+ struct sockaddr_in sin;
+ socklen_t sinlen;
+ struct mansession *s;
+ struct protoent *p;
+ int arg = 1;
+ int flags;
+ pthread_attr_t attr;
+ char iabuf[INET_ADDRSTRLEN];
+ int is_encrypted;
+
+ 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));
+ }
+ }
+
+ /* 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 )
+ 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));
+ continue;
+ }
+ memset(s, 0, sizeof(struct mansession));
+ memcpy(&s->sin, &sin, sizeof(sin));
+
+ /* For safety, make sure socket is non-blocking */
+ flags = fcntl(get_real_fd(as), F_GETFL);
+ fcntl(get_real_fd(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->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++;
+ break;
+ case 'h':
+ Usage();
+ exit(0);
+ case 'v':
+ Version();
+ exit(0);
+ case '?':
+ Usage();
+ exit(1);
+ }
+ }
+
+
+ ReadConfig();
+ proxylog = OpenLogfile();
+ debugmsg("loading handlers");
+ LoadHandlers();
+ debugmsg("loaded handlers");
+
+ 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 SSL Client-Side Context */
+ client_init_secure();
+
+ /* 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..b87d564
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,146 @@
+/* Asterisk Manager Proxy
+ Copyright (c) 2005-2006 David C. Troy <dave@popvox.com>
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License.
+
+ common.c
+ contains common utililty functions used by both astmanproxy
+ core as well as (many) of the various I/O handlers
+*/
+
+#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];
+
+ /* Look for \r\n from the front, our preferred end of line */
+ for (x=0;x<s->inlen;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;
+ }
+ /* get actual fd, even if a negative SSL fd */
+ fds[0].fd = get_real_fd(s->fd);
+
+ fds[0].events = POLLIN;
+ 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) {
+ 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;
+ s->inbuf[s->inlen] = '\0';
+ return 0;
+}
+
+char *astman_get_header(struct message *m, char *var)
+{
+ char cmp[80];
+ int x;
+ snprintf(cmp, sizeof(cmp), "%s: ", var);
+ for (x=0;x<m->hdrcount;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);
+}
+
+
+/*! 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/config.c b/src/config.c
new file mode 100644
index 0000000..4a9ce2b
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,313 @@
+/* Asterisk Manager Proxy
+ Copyright (c) 2005-2006 David C. Troy <dave@popvox.com>
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License.
+
+ config.c
+ routines to read and parse the configuration file and initialize
+ the internal configuration datastructures
+*/
+
+#include <pwd.h>
+#include <grp.h>
+#include "astmanproxy.h"
+
+extern struct iohandler *iohandlers;
+
+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));
+ if ( !srv ) {
+ fprintf(stderr, "Failed to allocate server credentials: %s\n", strerror(errno));
+ exit(1);
+ }
+ memset(srv, 0, sizeof (struct ast_server) );
+ memset(usessl, 0, sizeof (usessl) );
+
+ 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;
+ case 5:
+ strncat(usessl, s, 1);
+ break;
+ }
+ } while (*(s++));
+
+
+ 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);
+ }
+
+ srv->use_ssl = (!strcmp(usessl,"on"));
+ srv->next = pc.serverlist;
+ pc.serverlist = srv;
+
+ return 0;
+}
+
+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,"asteriskwritetimeout") )
+ 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,"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") )
+ 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") )
+ 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->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 );
+
+ /* Set nonzero config defaults */
+ pc.asteriskwritetimeout = 100;
+ pc.clientwritetimeout = 100;
+ pc.sslclhellotimeout = 500;
+
+ 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);
+
+ /* initialize SSL layer with our server certfile */
+ init_secure(pc.certfile);
+
+ 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..647e36b
--- /dev/null
+++ b/src/config_perms.c
@@ -0,0 +1,135 @@
+/* Asterisk Manager Proxy
+ Copyright (c) 2005-2006 David C. Troy <dave@popvox.com>
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License.
+
+ config_perms.c
+ routines to read and parse the astmanproxy.users file
+*/
+
+#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..bcd9a88
--- /dev/null
+++ b/src/csv.c
@@ -0,0 +1,31 @@
+/* Asterisk Manager Proxy
+ Copyright (c) 2005-2006 David C. Troy <dave@popvox.com>
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License.
+
+ csv.c
+ 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; i<m->hdrcount; i++) {
+ sprintf(outstring, "\"%s\"", m->headers[i]);
+ if (i<m->hdrcount-1)
+ strcat(outstring, ", ");
+ ast_carefulwrite(s->fd, outstring, strlen(outstring), s->writetimeout);
+ }
+ ast_carefulwrite(s->fd, "\r\n\r\n", 4, s->writetimeout);
+ pthread_mutex_unlock(&s->lock);
+
+ 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 <jacereda@users.sourceforge.net> &
+ Peter O'Gorman <ogorman@users.sourceforge.net>
+
+Portions may be copyright others, see the AUTHORS file included with this
+distribution.
+
+Maintained by Peter O'Gorman <ogorman@users.sourceforge.net>
+
+Bug Reports and other queries should go to <ogorman@users.sourceforge.net>
+
+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 <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <mach-o/dyld.h>
+#include <mach-o/nlist.h>
+#include <mach-o/getsect.h>
+/* 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 <ddavidso AT apple DOT com>
+ * 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..3dc874f
--- /dev/null
+++ b/src/http.c
@@ -0,0 +1,238 @@
+/* Asterisk Manager Proxy
+ Copyright (c) 2005-2006 David C. Troy <dave@popvox.com>
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License.
+
+ http.c
+ HTTP Input Handler
+*/
+
+#include "astmanproxy.h"
+
+// SwapChar: This routine swaps one character for another
+void SwapChar(char *pOriginal, char cBad, char cGood) {
+ int i; // generic counter variable
+
+ // Loop through the input string (cOriginal), character by
+ // character, replacing each instance of cBad with cGood
+
+ i = 0;
+ while (pOriginal[i]) {
+ if (pOriginal[i] == cBad) pOriginal[i] = cGood;
+ i++;
+ }
+}
+
+// IntFromHex: A subroutine to unescape escaped characters.
+static int IntFromHex(char *pChars) {
+ int Hi; // holds high byte
+ int Lo; // holds low byte
+ int Result; // holds result
+
+ // Get the value of the first byte to Hi
+
+ Hi = pChars[0];
+ if ('0' <= Hi && Hi <= '9') {
+ Hi -= '0';
+ } else
+ if ('a' <= Hi && Hi <= 'f') {
+ Hi -= ('a'-10);
+ } else
+ if ('A' <= Hi && Hi <= 'F') {
+ Hi -= ('A'-10);
+ }
+
+ // Get the value of the second byte to Lo
+
+ Lo = pChars[1];
+ if ('0' <= Lo && Lo <= '9') {
+ Lo -= '0';
+ } else
+ if ('a' <= Lo && Lo <= 'f') {
+ Lo -= ('a'-10);
+ } else
+ if ('A' <= Lo && Lo <= 'F') {
+ Lo -= ('A'-10);
+ }
+ Result = Lo + (16 * Hi);
+ return (Result);
+}
+
+// URLDecode: This routine loops through the string pEncoded
+// (passed as a parameter), and decodes it in place. It checks for
+// escaped values, and changes all plus signs to spaces. The result
+// is a normalized string. It calls the two subroutines directly
+// above in this listing, IntFromHex() and SwapChar().
+
+void URLDecode(char *pEncoded) {
+ char *pDecoded; // generic pointer
+
+ // First, change those pesky plusses to spaces
+ SwapChar (pEncoded, '+', ' ');
+
+ // Now, loop through looking for escapes
+ pDecoded = pEncoded;
+ while (*pEncoded) {
+ if (*pEncoded=='%') {
+ // A percent sign followed by two hex digits means
+ // that the digits represent an escaped character. We
+ // must decode it.
+
+ pEncoded++;
+ if (isxdigit(pEncoded[0]) && isxdigit(pEncoded[1])) {
+ *pDecoded++ = (char) IntFromHex(pEncoded);
+ pEncoded += 2;
+ }
+ } else {
+ *pDecoded ++ = *pEncoded++;
+ }
+ }
+ *pDecoded = '\0';
+}
+
+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) {
+ /* 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);
+ 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) ) {
+ URLDecode(formdata);
+ res = ParseHTTPInput(formdata, m);
+ return res;
+ } else {
+ pthread_mutex_lock(&s->lock);
+ s->outputcomplete = 1;
+ pthread_mutex_unlock(&s->lock);
+ return 0;
+ }
+ }
+ }
+ 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..b7d1629
--- /dev/null
+++ b/src/include/astmanproxy.h
@@ -0,0 +1,148 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <errno.h>
+#include <dlfcn.h>
+#ifdef __APPLE__
+ #include "poll-compat.h"
+#else
+ #include <sys/poll.h>
+#endif
+
+#define BUFSIZE 1024
+#define MAX_HEADERS 256
+#define MAX_LEN 1024
+
+#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 use_ssl; /* Use SSL when Connecting to Server? */
+ 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; /* enable autofiltering? */
+ int authrequired; /* is authentication required? */
+ char key[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 acceptunencryptedconnection; /* accept unencrypted connections? */
+ char certfile[256]; /* our SERVER-side SSL certificate file */
+};
+
+struct iohandler {
+ int (*read) ();
+ int (*write) ();
+ int (*onconnect) ();
+ 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 authenticated;
+ 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];
+ char challenge[10]; /*! Authentication challenge */
+ int writetimeout; /* Timeout for ast_carefulwrite() */
+ 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 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/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 <jacereda@users.sourceforge.net> &
+ Peter O'Gorman <ogorman@users.sourceforge.net>
+
+Portions may be copyright others, see the AUTHORS file included with this
+distribution.
+
+Maintained by Peter O'Gorman <ogorman@users.sourceforge.net>
+
+Bug Reports and other queries should go to <ogorman@users.sourceforge.net>
+
+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/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 <markster@digium.com>
+ *
+ * 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 <endian.h>
+#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#if defined(__OpenBSD__)
+#include <machine/types.h>
+#endif /* __OpenBSD__ */
+#include <machine/endian.h>
+#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/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 <inttypes.h>
+
+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/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 <bmc@clapper.org>"
+ 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/include/ssl.h b/src/include/ssl.h
new file mode 100644
index 0000000..123bd43
--- /dev/null
+++ b/src/include/ssl.h
@@ -0,0 +1,89 @@
+/*
+ * 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 <openssl/ssl.h>
+#include "astmanproxy.h"
+
+int connect_nonb(struct mansession *a);
+
+/*! \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 16
+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/log.c b/src/log.c
new file mode 100644
index 0000000..fc86643
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,67 @@
+/* Asterisk Manager Proxy
+ Copyright (c) 2005-2006 David C. Troy <dave@popvox.com>
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License.
+
+ log.c
+ Log & debug routines
+*/
+
+#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/md5.c b/src/md5.c
new file mode 100644
index 0000000..cda3441
--- /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 "endian.h"
+#include "astmanproxy.h"
+#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<<s | 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
diff --git a/src/poll.c b/src/poll.c
new file mode 100644
index 0000000..4e7cda3
--- /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 <bmc@clapper.org>"
+ 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 <unistd.h> /* standard Unix definitions */
+#include <sys/types.h> /* system types */
+#include <sys/time.h> /* time definitions */
+#include <assert.h> /* assertion macros */
+#include <string.h> /* 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..484da78
--- /dev/null
+++ b/src/proxyfunc.c
@@ -0,0 +1,398 @@
+/* Asterisk Manager Proxy
+ Copyright (c) 2005-2006 David C. Troy <dave@popvox.com>
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License.
+
+ proxyfunc.c
+ Functions specific to the manager proxy, not found in standard Asterisk AMI
+*/
+
+#include "astmanproxy.h"
+#include "md5.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);
+
+ memset(&mo, 0, sizeof(struct message));
+ AddHeader(&mo, "ProxyResponse: Success");
+ AddHeader(&mo, "OutputFormat: %s", s->output->formatname );
+
+ s->output->write(s, &mo);
+
+ 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: %s", s->challenge);
+
+ 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;
+}
+
+int AuthMD5(char *key, char *challenge, char *password) {
+ int x;
+ int len=0;
+ char md5key[256] = "";
+ struct MD5Context md5;
+ unsigned char digest[16];
+
+ 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;
+ else
+ return 1;
+}
+
+void *ProxyLogin(struct mansession *s, struct message *m) {
+ struct message mo;
+ struct proxy_user *pu;
+ char *user, *secret, *key;
+
+ user = astman_get_header(m, "Username");
+ secret = astman_get_header(m, "Secret");
+ key = astman_get_header(m, "Key");
+
+ memset(&mo, 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) ) {
+ if (!AuthMD5(key, s->challenge, pu->secret) || !strcmp(secret, pu->secret) ) {
+ AddHeader(&mo, "Response: Success");
+ AddHeader(&mo, "Message: Authentication accepted");
+ s->output->write(s, &mo);
+ 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;
+ }
+ }
+ pu = pu->next;
+ }
+ pthread_mutex_unlock(&userslock);
+
+ 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;
+}
+
+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) );
+ 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( pc.authrequired && !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, char *errmsg) {
+ struct message m;
+
+ memset(&m, 0, sizeof(struct message));
+ AddHeader(&m, "Response: Error");
+ AddHeader(&m, "Message: %s", errmsg);
+
+ s->output->write(s, &m);
+
+ return 0;
+}
diff --git a/src/ssl.c b/src/ssl.c
new file mode 100644
index 0000000..40115b1
--- /dev/null
+++ b/src/ssl.c
@@ -0,0 +1,422 @@
+/*
+ * 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.
+
+ 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.
+*/
+#include <sys/types.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include "ssl.h"
+
+SSL_CTX *sctx;
+SSL_CTX *cctx;
+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;
+}
+
+
+/* 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)
+ debugmsg("Failed to create a client ssl context!");
+ else
+ debugmsg("Client SSL Context Initialized");
+ 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 <SEC_MAX)
+ fd = sec_channel[fd].fd;
+ else fd = -1;
+
+ }
+ return fd;
+}
+
+/*! \brief Returns the SSL pointer from the fd. This structure is filled when we accept
+ * the ssl connection and used
+ * for reading and writing through ssl.
+*/
+SSL *get_ssl(int fd)
+{
+ SSL *ssl = NULL;
+
+ fd = -fd - 2;
+
+ if (fd>=0 && fd <SEC_MAX)
+ ssl = sec_channel[fd].ssl;
+
+ return ssl;
+}
+
+/*! \brief Returns the empty ssl slot. Used to save ssl information.
+*/
+int sec_getslot(void)
+{
+ int i;
+
+ for (i=0; i<SEC_MAX; i++) {
+ if(sec_channel[i].ssl==NULL)
+ break;
+ }
+
+ if (i==SEC_MAX)
+ return -1;
+ return i;
+}
+
+/*! \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).
+ * The positive fd's are required for system calls.
+ *
+*/
+int saccept(int s)
+{
+ int fd, err;
+ SSL* ssl;
+
+ if (!ssl_initialized)
+ return s;
+
+ if (((fd=sec_getslot())!=-1)) {
+ ssl=SSL_new(sctx);
+ SSL_set_fd(ssl, s);
+ sec_channel[fd].ssl = ssl; /* remember ssl */
+ sec_channel[fd].fd = s; /* remember the real fd */
+ do {
+ err = SSL_accept(ssl);
+ err = SSL_get_error(ssl, err);
+ } while( err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE);
+
+ SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
+ debugmsg("ssl_addon: Connection accepted");
+
+ err=1;
+
+ fd = -(fd+2);
+
+ if (err!=1 || !ssl) {
+ /* it did not work */
+ sec_channel[fd].ssl = NULL; /* free the slot */
+ fd = -1;
+ }
+ }
+ return fd;
+}
+
+/*!
+ * \brief Writes through secured ssl connection
+*/
+int m_send(int fd, const void *data, size_t len)
+{
+ sent_bytes += len;
+
+ if (fd < -1) {
+ SSL* ssl = get_ssl(fd);
+ return SSL_write(ssl, data, len);
+ }
+ return write(fd, data, len);
+}
+
+/*!
+ * \brief Receives data from the SSL connection.
+*/
+int m_recv(int s, void *buf, size_t len, int flags)
+{
+ int ret = 0;
+
+ if (s<-1) {
+ SSL* ssl = get_ssl(s);
+ ret = SSL_read (ssl, buf, len);
+ } else
+ ret = recv(s, buf, len, flags);
+
+ if (ret > 0)
+ rec_bytes += ret;
+
+ if (debug && s<-1)
+ debugmsg("Received %d bytes from SSL socket", 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(get_real_fd(socket));
+
+ if (ssl)
+ SSL_free (ssl);
+
+ return(ret);
+}
+
+/*! \brief This process cannot continue without fixing this error.
+*/
+int errexit(char s[])
+{
+ debugmsg("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 ) {
+ debugmsg("is_encrypt_request: select returned error, This should not happen:");
+ 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 (debug)
+ 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()");
+ return 1;
+ }
+ /* 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;
+ }
+ }
+ return 0;
+}
+
+
+/* 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) {
+ 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 */
+
+ 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 */
+ } 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 */
+ fd = -1;
+ }
+ }
+
+ debugmsg("returning ast_connect with %d", fd);
+ pthread_mutex_lock(&a->lock);
+ a->fd = fd;
+ pthread_mutex_unlock(&a->lock);
+
+ return fd;
+}
+
+int connect_nonb(struct mansession *a)
+{
+ 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);
+ }
+
+ /* 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(sockfd);
+}
diff --git a/src/standard.c b/src/standard.c
new file mode 100644
index 0000000..f377bec
--- /dev/null
+++ b/src/standard.c
@@ -0,0 +1,70 @@
+/* Asterisk Manager Proxy
+ Copyright (c) 2005-2006 David C. Troy <dave@popvox.com>
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License.
+
+ standard.c
+ 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;
+
+ for (;;) {
+ res = get_input(s, 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;
+
+ pthread_mutex_lock(&s->lock);
+ for (i=0; i<m->hdrcount; i++) {
+ ast_carefulwrite(s->fd, m->headers[i], strlen(m->headers[i]) , s->writetimeout);
+ ast_carefulwrite(s->fd, "\r\n", 2, s->writetimeout);
+ }
+ ast_carefulwrite(s->fd, "\r\n", 2, s->writetimeout);
+ 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);
+ 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
new file mode 100644
index 0000000..f068b73
--- /dev/null
+++ b/src/xml.c
@@ -0,0 +1,159 @@
+/* Asterisk Manager Proxy
+ Copyright (c) 2005-2006 David C. Troy <dave@popvox.com>
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License.
+
+ xml.c
+ XML I/O Handler
+*/
+
+#include "astmanproxy.h"
+
+#define XML_UNPARSED "UnparsedText"
+#define XML_BEGIN_INPUT "<AsteriskManagerInput>"
+#define XML_END_INPUT "</AsteriskManagerInput>"
+
+#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);
+ ast_carefulwrite(s->fd, buf, strlen(buf), s->writetimeout);
+
+ for (i=0; i<m->hdrcount; i++) {
+ memset(xmlescaped, 0, sizeof xmlescaped);
+ xml_quote_string(m->headers[i], xmlescaped);
+ lpos = xmlescaped;
+ dpos = strstr(lpos, ": ");
+ if (dpos && *(lpos)!= ' ' && strlen(xmlescaped)<30 ) {
+ 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);
+ ast_carefulwrite(s->fd, outstring, strlen(outstring), s->writetimeout);
+ }
+ sprintf(buf, "</%s>\r\n\r\n", xmldoctag);
+ ast_carefulwrite(s->fd, buf, strlen(buf), s->writetimeout);
+ pthread_mutex_unlock(&s->lock);
+
+ 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, "&lt;");
+ else if (*c == '>')
+ strcat(o, "&gt;");
+ else if (*c == '&')
+ strcat(o, "&amp;");
+ else if (*c == '"')
+ strcat(o, "&quot;");
+ 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;
+}