├── INSTALL ├── Makefile ├── README ├── TODO ├── VERSIONS ├── configs ├── astmanproxy.conf ├── astmanproxy.users └── ssl.conf ├── debian ├── astmanproxy-default.ex ├── astmanproxy.1 ├── astmanproxy.doc-base.EX ├── changelog ├── compat ├── conffiles ├── control ├── copyright ├── dirs ├── docs ├── init.d.ex ├── postinst.ex ├── postrm.ex ├── preinst.ex ├── prerm.ex ├── rules └── watch ├── doc ├── README.csv ├── README.http ├── README.standard └── README.xml ├── samples ├── httpast.html └── httpast2.html └── src ├── astmanproxy.c ├── common.c ├── config.c ├── config_perms.c ├── csv.c ├── dlfcn.c ├── http.c ├── include ├── astmanproxy.h ├── dlfcn-compat.h ├── endian.h ├── md5.h ├── poll-compat.h └── ssl.h ├── log.c ├── md5.c ├── poll.c ├── proxyfunc.c ├── ssl.c ├── standard.c └── xml.c /INSTALL: -------------------------------------------------------------------------------- 1 | Quick Install Instructions 2 | -------------------------- 3 | 4 | Requirements: openssl, openssl-devel 5 | 6 | Installation is easy: 7 | 8 | 1. make 9 | 2. make install 10 | 3. To run: 11 | 12 | astmanproxy -d (for debug mode) 13 | astmanproxy -dddddd (for more debug info) 14 | astmanproxy (for background mode) 15 | 16 | Other make actions: 17 | 18 | make cert Makes initial proxy server certificate 19 | make certificate Forces remake of proxy server certificate) 20 | make clean Remove all binary object files from source tree 21 | 22 | Please read README and docs/README.* for info on specific modules. 23 | 24 | Enjoy! 25 | 26 | -------------------------------------------- 27 | (C) 2005-2008 David C. Troy, dave@popvox.com 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .EXPORT_ALL_VARIABLES: 2 | 3 | OSARCH=$(shell uname -s) 4 | OSREV=$(shell uname -r) 5 | 6 | VERSION := 1.22pre081119 7 | DESTDIR ?= 8 | CONFDIR:=/etc/asterisk 9 | CONFDIR_REAL := $(DESTDIR)/etc/asterisk 10 | PERMDIR:=/etc/asterisk 11 | PERMDIR_REAL := $(DESTDIR)/etc/asterisk 12 | DISTDIR:=/var/www/html/astmanproxy 13 | 14 | LIBDIR := $(DESTDIR)/usr/lib/astmanproxy 15 | CONFFILE := astmanproxy.conf 16 | PERMFILE := astmanproxy.users 17 | 18 | CC := gcc 19 | INCLUDES := 20 | PREFIX:= /usr/local 21 | BINDIR := $(DESTDIR)$(PREFIX)/sbin 22 | 23 | # For compilation dependencies 24 | MODS := astmanproxy config config_perms common proxyfunc log ssl md5 25 | HANDLERS := xml standard csv http 26 | SOBJS := $(HANDLERS:%=%.so) 27 | LIBS := -lssl 28 | 29 | # Add -g below for debug/GDB symbols 30 | CFLAGS:=-Wall -O2 -D_REENTRANT -fPIC -Isrc/include -I/usr/include/openssl 31 | 32 | ifeq (${OSARCH},Darwin) 33 | LIBS+=-lresolv 34 | CFLAGS+=-D__Darwin_ -Iquotesrc/include 35 | BINDIR=/opt/sbin 36 | LIBDIR=/opt/lib/astmanproxy 37 | CONFDIR=/opt/etc/asterisk 38 | CONFDIR_REAL=/opt/etc/asterisk 39 | PERMDIR=/opt/etc/asterisk 40 | PERMDIR_REAL=/opt/etc/asterisk 41 | LOGDIR=/opt/log/asterisk 42 | CERTDIR := /opt/lib/asterisk/certs 43 | ifeq (${OSREV},7.9.0) 44 | OBJS+=poll.o dlfcn.o 45 | endif 46 | ASTLINK=-Wl,-force_flat_namespace,-dynamic 47 | SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace 48 | MKTEMP=/usr/bin/mktemp 49 | else 50 | #These are used for all but Darwin 51 | CFLAGS+=-I- 52 | LIBS+=-ldl -pthread 53 | ASTLINK=-Wl,-E 54 | SOLINK=-shared -Xlinker -x 55 | LOGDIR=/var/log/asterisk 56 | CERTDIR := /var/lib/asterisk/certs 57 | MKTEMP=/bin/mktemp 58 | endif 59 | 60 | MODDIR := $(LIBDIR)/modules 61 | DEFINES:='-DPROXY_VERSION="$(VERSION)"' '-DCDIR="$(CONFDIR)"' '-DCFILE="$(CONFFILE)"' 62 | DEFINES+='-DMDIR="$(MODDIR)"' '-DPDIR="$(PERMDIR)"' '-DPFILE="$(PERMFILE)"' 63 | 64 | PROXYCERT := $(CERTDIR)/proxy-server.pem 65 | PROXYSSLCONF := $(CONFDIR)/proxy-ssl.conf 66 | 67 | CFLAGS += $(DEFINES) 68 | 69 | OBJS += $(MODS:%=%.o) 70 | CONF_TARGET:= $(CONFDIR_REAL)/$(CONFFILE) 71 | PERM_TARGET:= $(PERMDIR_REAL)/$(PERMFILE) 72 | VPATH = src 73 | 74 | 75 | # For printing only 76 | SRCS := $(MODS:%=src/%.c) 77 | HDRS := src/include/astmanproxy.h 78 | 79 | all: astmanproxy cert 80 | 81 | astmanproxy: $(OBJS) $(SOBJS) 82 | $(CC) $(CFLAGS) -o $@ $(ASTLINK) $(OBJS) $(LIBS) 83 | 84 | $(OBJS): %.o: %.c 85 | $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< 86 | 87 | $(SOBJS): %.so: %.o 88 | $(CC) $(SOLINK) $< -o $@ 89 | 90 | SERIAL=`date "+%Y%m%d%H%M%S"` 91 | 92 | cert: 93 | if [ ! -f $(PROXYCERT) ]; then \ 94 | umask 77 ; \ 95 | PEM1=`$(MKTEMP) /tmp/openssl.XXXXXX` ; \ 96 | PEM2=`$(MKTEMP) /tmp/openssl.XXXXXX` ; \ 97 | if [ ! -f $(PROXYSSLCONF) ]; then \ 98 | install ./configs/ssl.conf $(PROXYSSLCONF); \ 99 | fi; \ 100 | /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) -config $(PROXYSSLCONF) ; \ 101 | mkdir -p $(CERTDIR); \ 102 | cat $$PEM1 > $(PROXYCERT) ; \ 103 | echo "" >> $(PROXYCERT) ; \ 104 | cat $$PEM2 >> $(PROXYCERT) ; \ 105 | rm $$PEM1 $$PEM2; \ 106 | fi 107 | 108 | certificate: 109 | createcert="1"; \ 110 | if [ -f $(PROXYCERT) ]; then \ 111 | echo -n "The certificate already exists, Do you really want to create new one(yes/no)?"; \ 112 | read answer; \ 113 | if [ "$$answer" = "yes" ]; then \ 114 | echo "I am creating a new certificate, Old one is copied as server.pem.old ";\ 115 | sudo cp /var/lib/asterisk/certs/server.pem /var/lib/asterisk/certs/server.pem.old; \ 116 | elif [ "$$answer" = "no" ]; then \ 117 | echo "Certificate already exists, I am not creating a new certificate,";\ 118 | createcert="0"; \ 119 | else \ 120 | echo "You need to enter either yes or no"; \ 121 | createcert="0"; \ 122 | fi; \ 123 | fi; \ 124 | if [ "$$createcert" = "1" ]; then \ 125 | umask 77 ; \ 126 | PEM1=`$(MKTEMP) /tmp/openssl.XXXXXX` ; \ 127 | PEM2=`$(MKTEMP) /tmp/openssl.XXXXXX` ; \ 128 | if [ ! -f $(PROXYSSLCONF) ]; then \ 129 | install ./configs/ssl.conf $(PROXYSSLCONF); \ 130 | fi; \ 131 | /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) -config $(PROXYSSLCONF) ; \ 132 | mkdir -p $(CERTDIR); \ 133 | cat $$PEM1 > $(PROXYCERT) ; \ 134 | echo "" >> $(PROXYCERT) ; \ 135 | cat $$PEM2 >> $(PROXYCERT) ; \ 136 | rm $$PEM1 $$PEM2; \ 137 | fi 138 | 139 | 140 | install: uninstall all 141 | install -d $(BINDIR) 142 | install astmanproxy $(BINDIR) 143 | install -d $(LIBDIR) 144 | install -d $(MODDIR) 145 | install $(SOBJS) $(MODDIR) 146 | install -d $(CONFDIR_REAL) 147 | if [ ! -f $(CONF_TARGET) ]; then \ 148 | install ./configs/$(CONFFILE) $(CONF_TARGET); \ 149 | fi 150 | if [ ! -f $(PERM_TARGET) ]; then \ 151 | install ./configs/$(PERMFILE) $(PERM_TARGET); \ 152 | fi 153 | @echo "Installation Complete!" 154 | 155 | uninstall: 156 | rm -f $(BINDIR)/astmanproxy 157 | cd $(MODDIR); rm -f $(SOBJS) 158 | @echo "Successfully uninstalled!" 159 | 160 | dist: clean 161 | rm -rf /tmp/astmanproxy-${VERSION}*; \ 162 | cp -R . /tmp/astmanproxy-${VERSION}; \ 163 | cd /tmp; tar czf /tmp/astmanproxy-${VERSION}-`date +%Y%m%d-%H%M`.tgz astmanproxy-${VERSION}; \ 164 | /usr/bin/scp /tmp/astmanproxy-${VERSION}-*.tgz root@www.popvox.com:$(DISTDIR); \ 165 | /usr/bin/ssh -lroot www.popvox.com "ln -sf $(DISTDIR)/astmanproxy-${VERSION}-*.tgz $(DISTDIR)/astmanproxy-latest.tgz" 166 | 167 | clean: 168 | rm -f *.o *.so core *~ astmanproxy proxy-server.pem; 169 | 170 | print: 171 | more Makefile $(HDRS) $(SRCS) | enscript -Ec -2r -j; exit 0 172 | @echo "Printing Complete!" 173 | 174 | love: 175 | @echo "Here? Now?" 176 | 177 | # DO NOT DELETE 178 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | astmanproxy README 2 | (c) 2005-2008 David C. Troy, dave@popvox.com 3 | ------------------------------------------------------------------ 4 | FOREWORD & QUICK START 5 | 6 | The need for a proxy to Asterisk's manager interface has been 7 | clear; almost all GUIs and other interfaces to asterisk implement a 8 | proxy of some kind. Why? A proxy offers: 9 | 10 | - A single persistent connection to asterisk 11 | - A more secure (non-root) TCP interface 12 | - Ability to offer filtered input/output 13 | - Less connections and networking load for asterisk 14 | 15 | It can serve as the basis for an extensible application framework 16 | for communication with multiple Asterisk servers. 17 | 18 | Features include: 19 | 20 | - Multiple Input/Output formats: HTTP, XML, CSV, and Standard 21 | - SSL Support for clients & servers (including HTTPS clients) 22 | - API for addition of new, modular I/O formats 23 | - Ability to support communication with multiple Asterisk Servers 24 | - I/O Formats selectable on a per-client basis 25 | - Written in c/pthreads to be fast and robust 26 | 27 | You can use Astmanproxy as the basis for a web-based application: 28 | send it data using HTTP POST or HTTP GET, and receive XML output. 29 | Or use HTTP POST and get Standard (text/plain) output back! 30 | Astmanproxy speaks HTTP internally, so no web server is required! 31 | 32 | You can use Astmanproxy as an XML feed for a .NET program that keeps 33 | track of Asterisk's state. Or as an interface for injecting quick 34 | commands into multiple Asterisk boxes from your Python scripts. The 35 | possibilities are limited only by your imagination. 36 | 37 | To get started quickly, simply: 38 | make 39 | make install 40 | 41 | Edit the configuration file: 42 | vi /etc/asterisk/astmanproxy.conf 43 | 44 | Optionally edit the other config files: 45 | vi /etc/asterisk/astmanproxy.users 46 | vi /etc/asterisk/ssl.conf 47 | 48 | Start the program: 49 | astmanproxy 50 | 51 | To view debug output, start astmanproxy in debug mode: 52 | astmanproxy -d 53 | 54 | For more debug output, add more -d's: 55 | astmanproxy -ddddddd 56 | 57 | You may want to start astmanproxy at boot. In that case, you might 58 | place it in /etc/rc.d/rc.local: 59 | /usr/local/sbin/astmanproxy 60 | 61 | Please send your feedback! We are looking for contributors to add 62 | support for new I/O formats and add new features! 63 | 64 | Contributions: 65 | Paypal via dave@popvox.com; beer accepted at Astricon events 66 | 67 | =================================================================== 68 | Additional Proxy Features 69 | 70 | In addition to exposing the entire Asterisk Manager API as a 71 | pass-through, non-interpreting proxy, 'astmanproxy' can parse client 72 | input where desired; this could be used in the future to add new 73 | features that should exist in a proxy but that don't 74 | necessarily need to be in Asterisk proper. 75 | 76 | There are some proxy-specific headers that you can specify in your 77 | applications now: 78 | 79 | Server: (x.x.x.x|hostname) 80 | Specify a server to which to send your commands. This should match 81 | the server name specified in your config file's "host" entry. 82 | If you do not specify a server, the proxy will pick the first one 83 | it finds -- fine in single-server configurations. 84 | 85 | Some "ProxyActions" have been implemented which the Proxy responds to 86 | but does not pass on to Asterisk itself: 87 | 88 | ProxyAction: ListSessions 89 | Outputs a list of all active client and server sessions 90 | 91 | ProxyAction: SetOutputFormat 92 | OutputFormat: (standard|xml) 93 | Sets the output format on a per-client basis 94 | 95 | ProxyAction: SetAutoFilter 96 | AutoFilter: (on|off|unique) 97 | Sets the AutoFilter property on a per-client basis 98 | (See autofiltering section below) 99 | 100 | ProxyAction: Logoff 101 | Logs client out of Proxy and leaves Asterisk alone. 102 | 103 | ProxyAction: ListIOHandlers 104 | Lists all available Input/Output Handlers 105 | Examples include Standard, XML, and CSV; more I/O 106 | formats may be added by creating new handler modules. 107 | 108 | ProxyAction: AddServer 109 | Server: (x.x.x.x|hostname) 110 | Port: (5038|other) 111 | Username: (username) 112 | Secret: (secret) 113 | Events: (on|off) 114 | Initiates a proxy connection to a new Asterisk Server; this 115 | has the same effect of including a host entry in your 116 | host= section of the configuration file. 117 | 118 | ProxyAction: DropServer 119 | Server: (x.x.x.x|hostname) 120 | Disconnects a proxy<->server session. Hostname specified 121 | should exactly match the entry in your config host= section, 122 | or whatever name you used with ProxyAction: AddServer. 123 | 124 | ProxyKey: secret 125 | Action: Originate 126 | ... 127 | ActionID: ... 128 | You can use this as a simple authentication mechanism. 129 | Rather than have to login with a username & password, 130 | you can specify a ProxyKey that must be passed from 131 | a client before requests are processed. This is helpful 132 | in situations where you would like to authenticate and 133 | execute an action in a single step. See the sample 134 | config file for more information. 135 | 136 | The proxy also intercepts the following Actions: 137 | 138 | Action: Login 139 | You can login to astmanproxy just as you would the Asterisk 140 | Manager Interface. The user credentials are stored in 141 | astmanproxy.users. 142 | 143 | Action: Challenge 144 | Astmanproxy now supports the MD5 challenge authentication 145 | mechanism. See section below for more information on 146 | this authentication mechanism and how you can use it 147 | in your applications to avoid having to send a password 148 | over the internet, and instead use a MD5 challenge to 149 | hash your password before sending. Note that this is 150 | somewhat less of an issue with SSL support now enabled, 151 | however, some apps require this mechanism, and we support it. 152 | 153 | Action: Logoff 154 | You don't want your applications logging the proxy off of 155 | Asterisk. The proxy intercepts "Action: Logoff" and interprets 156 | it as "ProxyAction: Logoff". This keeps the proxy from 157 | disconnecting from Asterisk. 158 | 159 | Blank Commands 160 | The proxy does not send commands to Asterisk until you have 161 | a fully formed Action block. This keeps unnecessary traffic 162 | and load off of Asterisk. The proxy intercepts and ignores 163 | blank command blocks. 164 | 165 | =================================================================== 166 | AstManProxy Autofiltering Functionality 167 | 168 | One of the most powerful features of AstManProxy is its ability to 169 | automatically filter output on a per-client basis. It can do this 170 | with its Autofilter capability, which can be set 'on'/'unique' in 171 | the config file or enabled via the ProxyAction: SetAutoFilter function. 172 | 173 | With autofiltering 'on', each client only receives output containing 174 | the "ActionID" parameter it has set most recently. This is useful 175 | for single atomic requests into asterisk from a client, such as 176 | when creating a simple UI to inject a command. 177 | 178 | For example, if a client sends this packet while autofiltering is 179 | enabled: 180 | 181 | Action: Ping 182 | ActionID: foo 183 | 184 | Then the autofilter ActionID for that client is set to foo, and no 185 | output besides for responses containing "foo" will be returned 186 | to that client, such as: 187 | 188 | Response: Pong 189 | ActionID: foo 190 | 191 | Replace Ping with Originate and Pong with Success and you can see 192 | how this same mechanism can be used to quickly query asterisk 193 | box(es), initiate calls, etc, without your client having to worry 194 | with filtering a lot of unrelated output. 195 | 196 | A more advanced verion of this facility is to set autofiltering to 197 | 'unique'. This causes astmanproxy to alter the ActionID on the way 198 | to Asterisk, and undo that change on the way back. 199 | 200 | For example the exchange: 201 | 202 | > Action: Ping 203 | > ActionID: foo 204 | > 205 | 206 | < Response: Pong 207 | < ActionID: foo 208 | < 209 | 210 | Might be seen by Asterisk as: 211 | 212 | > Action: Ping 213 | > ActionID: amp7-foo 214 | > 215 | 216 | < Response: Pong 217 | < ActionID: amp7-foo 218 | < 219 | 220 | and the "amp7-" prefix is created uniquely for each client connection. 221 | 222 | =================================================================== 223 | On the astmanproxy.users output filtering functionality 224 | 225 | Users may now be defined in your astmanproxy.users configuration file. 226 | This enables a traditional user/password based login mechanism 227 | for Astmanproxy similar to what is found in Asterisk. Output may be 228 | filtered on a per-user basis. 229 | 230 | "user" is the username, secret is the password, and the (optional) 231 | channel setting causes filtering of events only for the specified 232 | channel to be sent to this user. 233 | 234 | Following this, an outbound context and an inbound context may be 235 | (optionally) specified. This will cause messages to and from 236 | Asterisk respectively to be blocked if they contain a Context: header 237 | which does not match the specified value. This might be used to 238 | prevent a client making calls except in a predefined context. 239 | 240 | An account code may be (optionally) specified. This will 241 | force the Account: header to be overwritten for all commands to/from 242 | this client. If the Action is "Originate", then a missing Account: 243 | header will be added. 244 | 245 | A "server" option will cause the proxy to behave as if the 246 | client has included a "Server:" header in each request packet. 247 | 248 | Any non-empty string provided in "more_events" will allow the passing 249 | of non-filterable events to all clients. The default behaviour is to 250 | block these packets if any form of filtering is requested. 251 | 252 | user=secret,channel,out_context (to Asterisk),in_context (From Asterisk),accountcode,server,more_events 253 | 254 | e.g.: 255 | steve=steve,SIP/snom190,local, 256 | dave=securepass,SIP/1002,,,davesaccount,daveserver 257 | bill=pass 258 | 259 | =================================================================== 260 | On the 'Action: Challenge' Authentication Mechanism 261 | 262 | John Todd wrote this excellent summary of the Action: Challenge 263 | Authentication Mechanism, and it accurately describes the 264 | implementation included in astmanproxy: 265 | 266 | While the SSL encryption of the AMI is great, it's always a good 267 | policy to never send passwords at all if you have an alternative. 268 | 269 | After connecting to the AMI port, send this message: 270 | 271 | Action: Challenge 272 | AuthType: MD5 273 | 274 | You should receive a challenge string: 275 | 276 | Response: Success 277 | Challenge: 125065091 278 | 279 | Then, assuming that the manager username is "joebob" and the 280 | password is "yoyodyne11", perform this on a shell line of a handy 281 | UNIX system (you programmers will figure out how to do this with a 282 | library call, I'm sure): 283 | 284 | bash-3.00# md5 -s 125065091yoyodyne11 285 | MD5 ("125065091yoyodyne11") = e83a9e59e7c8d1bb6554982275d05016 286 | bash-3.00# 287 | 288 | Now use this key to log in, so type this to the AMI: 289 | 290 | Action: Login 291 | AuthType: MD5 292 | Username: joebob 293 | Key: e83a9e59e7c8d1bb6554982275d05016 294 | 295 | ...and you'll get: 296 | 297 | Response: Success 298 | Message: Authentication accepted 299 | 300 | =================================================================== 301 | On Astmanproxy's SSL Support 302 | 303 | Support for SSL on the Asterisk Manager Interface has recently been 304 | contributed to the Asterisk project (see Digium #6812). 305 | 306 | This SSL implementation has been tested by several people and seems 307 | to work fine. While it is not in a mainline Asterisk distribution 308 | yet (in SVN Trunk only right now), it is likely that AMI will soon 309 | support SSL natively. 310 | 311 | I felt that it was important that Astmanproxy support the same SSL 312 | mechanism as Asterisk; we have been talking about adding SSL/TLS 313 | for some time. So, now it's been incorporated. 314 | 315 | This means you can implement scenarios like: 316 | client <-> proxy <-> n*asterisk 317 | with end-to-end SSL security. 318 | 319 | To make Astmanproxy talk to asterisk, turn on the 'usessl' option 320 | in the server host specification (see astmanproxy.conf). 321 | 322 | To have Astmanproxy talk to clients via SSL, be sure to enable 323 | 'allowencryptedconnections' in the astmanproxy.conf file. 324 | 325 | To have Astmanproxy accept ONLY SSL connections, you should 326 | enable 'allowencryptedconnections' and disable 327 | 'allowunencryptedconnections'. We've endeavored to use the same 328 | configuration setting names as in manager.conf with the SSL 329 | implementation in #6812. 330 | 331 | =================================================================== 332 | Now Supports HTTPS Natively! 333 | 334 | One really interesting side effect of having both SSL and HTTP support 335 | natively is that we in fact now support HTTPS! 336 | 337 | With the proxy configured on localhost:1234, you can do things 338 | along these lines: 339 | 340 | https://localhost:1234/?Action=ShowChannels&ActionID=Foo 341 | 342 | This has been tested fairly extensively with good results. The 343 | HTTP handler supports both GET and POST and can properly deal 344 | with XML or Standard output formats. With Autofilter=on, 345 | this paradigm is ideal for creating a simple REST-like interface 346 | into Asterisk (even multiple boxes!) with no web servers needed. 347 | 348 | =================================================================== 349 | Software Updates, Author Info, and How to Contribute 350 | 351 | Current development on AstManProxy is happening here: 352 | http://github.com/davetroy/astmanproxy/tree/master 353 | 354 | Please feel free to fork and contribute! 355 | 356 | Also, there is a new mailing list / group available here: 357 | http://groups.google.com/group/astmanproxy?hl=en 358 | 359 | =================================================================== 360 | AstManProxy Background Information 361 | ---------------------------------- 362 | 363 | Developing web-based realtime applications for the asterisk 364 | open-source PBX often requires interacting with asterisk's Manager 365 | interface. The Asterisk Manager runs on port 5038 by default and 366 | provides a simple TCP interface into several common asterisk 367 | functions, including call origination, redirection, hangup, and many 368 | other functions. 369 | 370 | Each interaction from a web-based application requires a separate 371 | connection to the manager interface. Asterisk, being a real time 372 | application, shouldn't have to deal with the load of constant 373 | connections, disconnections, and authentications from a single 374 | trusted client, namely your web app. 375 | 376 | In the same way that web developers have solved this problem for 377 | other similar services (imapproxy for IMAP web mail clients, 378 | database connection caches, etc), 'astmanproxy' sets out to solve 379 | this problem for asterisk. 380 | 381 | This project started out as a simple proof-of-concept script called 382 | "simpleproxy.pl" which was made available in September 2004, 383 | following a discussion at the Astricon conference regarding the need 384 | for such a proxy. That code was based on Nicolas Gudino's manager 385 | proxy for his excellent Flash Operator Panel. Written in perl and 386 | as a single-threaded select-based "dumb" proxy, simpleproxy.pl has 387 | been widely used as a basis for experimentation, but I wanted 388 | something more robust and that could act as a basis for additional 389 | features. 390 | 391 | Asterisk Manager Proxy is a multithreaded proxy server for Asterisk 392 | Manager written in c, and based on several of the same data 393 | structures and routines found in the asterisk manager itself. This 394 | insures a very high degree of compatibility with Asterisk; it should 395 | also be very robust and scalable. 396 | 397 | Asterisk Manager Proxy gives the ability to support multiple input 398 | and output format -- implemented as abstracted I/O handlers -- and 399 | these are configurable on a per-client basis. 400 | 401 | =================================================================== 402 | (C) 2005-2008 David C. Troy, dave@popvox.com 403 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Some suggestions for features: 2 | trap action=command in xml.c; remove arbitrary detection of unparsed data 3 | write SOAP methods for http.c to use 4 | clean up reconnect to lost asterisk server (socket reuse) 5 | clean up debugmsg instances to add if (debug) 6 | deal with http non-conformity better, and act on POST MIME-type inputs 7 | State maintenance modules 8 | 9 | Some infrastructure ideas: 10 | Check for module versions; do not run without modules installed 11 | tcpwrappers/access control/connect mask? 12 | libtool/autoconf/automake support 13 | SetInputFormat proxyaction? 14 | -------------------------------------------------------------------------------- /VERSIONS: -------------------------------------------------------------------------------- 1 | 1.22pre081119 - Refresh of GIT fork by Steve Davies 2 | Add AutoFilter=unique option for better ActionID tracking (Steve Davies) 3 | Add more_events option for users to get more "noise" (Steve Davies) 4 | 5 | 1.22pre081111 - Refresh of GIT fork by Steve Davies 6 | Segfault amd SIGPIPE fixes (Steve Davies) 7 | Several fixes to UniqueID header tracking (Steve Davies) 8 | Ensure that Response: headers are not filtered out. Assumes ActionID is sent (Steve Davies) 9 | 10 | 1.22fork - GIT fork by Steve Davies for code merge 11 | Add "account" parameter to users file - Forces Account: header on Originate (Steve Davies) 12 | Add "server" parameter to users file - equivalent to Server: line in requests (Steve Davies) 13 | Allow multiple-logins by logging off when a 2nd Login action arrives (Steve Davies) 14 | (Helps compatability with some Manager Panel products) 15 | Include ActionID: in error responses and Login: responses (Steve Davies) 16 | (ActivaTSP and some other products require this) 17 | Client Timeout value was not being loaded (Wolfgang Pichler) 18 | Ensures that clients see all the right channel data (Steve Davies) 19 | Each server records a stack of NewChannel events 20 | Each client records which NewChannel events have been passed 21 | If a filtered session bridges channels, ensure that related NewChannel(s) are sent 22 | 23 | 1.22pre current trunk 24 | 25 | 1.21 Major code formatting cleanup and official release of 1.21pre (trunk) 26 | Documentation overhaul & cleanup 27 | 28 | 1.21pre Added URLDecode routine to http.c to deal with URL-encoded GET/POST data 29 | Fixed xml.c to better deal with cli/unparsed data 30 | Cleaned up Makefile for better support of Mac OS X 10.4 31 | Changed message to be dynamically allocated in HandleAsterisk; solved bus error on Mac OS X 10.4 32 | 33 | 1.20 Cleanup and official release of 1.20pre 34 | 35 | 1.20pre Now using ast_carefulwrite for non-blocking writes to all sockets 36 | Added asteriskwritetimeout config setting for asterisk write timeout 37 | Added clientwritetimeout config setting for client write timeout 38 | Added support for astmanproxy.users user authentication (Steve Davies) 39 | Added support for Action: Challenge/AuthType: MD5 authentication 40 | Added challenge field to mansession structure for use by Action: Challenge 41 | Added writetimeout var to mansession structure for use by ast_carefulwrite 42 | Added SSL Support (Remco Treffkorn, Mahesh Karoshi, John Todd; Tello Corp) 43 | Added 'usessl' option for connecting to asterisk servers 44 | Fixed proxykey pointer bug (Steve Davies) 45 | Ditched autodisconnect handler property in favor of inputcomplete/outputcomplete 46 | 47 | ------------------------------------------------------------------------------------------------ 48 | 1.1pre2 Completely Modularized and Abstracted Input/Output formats 49 | Cleaned up session write mutex code (Peter Loeppky) 50 | Fixed message initialization bug -- using wrong sizeof 51 | Added XML Input format 52 | Added HTTP Input format 53 | Added CSV Output format 54 | Support for connections to an arbitrary number of Asterisk servers 55 | Added retryinterval and maxretries to conf for reconnecting lost servers 56 | 57 | 1.1pre3 Added _autodisconnect property for I/O handlers 58 | Added _onconnect method for I/O handlers 59 | Returned connection banner to standard input handler via _onconnect method 60 | 61 | 1.1 Finalized /usr/lib/astmanproxy/modules for shared objects in Makefile 62 | Added make uninstall target 63 | Added fail on no I/O handlers loaded 64 | Updated README, added samples dir 65 | 1.11 Added ProxyAction ListServers (Richard Lyman) 66 | Fixed output bug, excess terminators (Dennis Persson) 67 | 1.12 Added support for bad/old-style Asterisk output (Ron Arts) 68 | Tweaked for compatibility with Flash Operator Panel (Nicolas Gudino) 69 | 1.13 Changed to read only /etc/asterisk/astmanproxy.conf and not ./astmanproxy.conf 70 | Added a 'connected' field so we don't try to write to servers which are not yet connected 71 | Exits when there are no servers able to connect 72 | Doesn't attempt to re-connect to a server if we get 'Authentication failed' 73 | Added a connection timeout by using connect_nonb 74 | Confirmed support for x86-64 processors (added -fPIC; Jennifer Hales) 75 | Added support for Mac OS X (Tested on 10.3.9); BSD may also work 76 | Aborts on old config file format (detects incomplete server spec) 77 | 78 | ------------------------------------------------------------------------------------------------ 79 | 0.99a Initial Beta Release 80 | 0.99b Moved WriteXMLClient into xml.c 81 | Fixed XML tag bug (Bryan Ballard) 82 | Fixed ast_log instances 83 | 1.0 First major release 84 | 1.0a Added minor Makefile changes 85 | Added Debian package support (Tzafrir Cohen) 86 | 1.0b Action: logoff (lowercase) was not intercepted properly (Steven Blatchford) 87 | Fixed SetOutputFormat documentation error (Steven Blatchford) 88 | 1.0c Intercept empty command blocks; do not pass to Asterisk 89 | Added call for proxyerror routine, previously unreferenced 90 | Intercept Action: Login; always authenticates and does not pass to * 91 | 1.0d Properly intercept SIGPIPE (Brian Jones) 92 | -------------------------------------------------------------------------------- /configs/astmanproxy.conf: -------------------------------------------------------------------------------- 1 | ; astmanproxy.conf 2 | ; Asterisk Manager Proxy Configuration Sample 3 | ; (C) 2005-2008 David C. Troy - dave@popvox.com 4 | 5 | ; List of asterisk host(s) you want to proxy 6 | ; host = ip_addr, port, user, secret, events, use_ssl 7 | host = localhost, 5038, dave, moo, on, off 8 | 9 | ;host = 192.168.1.173, 5038, dave, moo, on, on 10 | ;host = 127.0.0.2, 5038, user, secret, on 11 | ;host = otherhost, 5038, user, secret, on 12 | ;host = newhost, 5030, user, secret, off 13 | ;host = oldhost, 5040, user, secret, off 14 | ;host = myhost, 5038, user, secret, on 15 | 16 | ; Server reconnect interval (in seconds); how often to retry 17 | ; Connecting to an asterisk server whose connection was lost 18 | retryinterval = 2 19 | 20 | ; Number of times to retry connecting to a given server 21 | ; use 0 for infinitely, or some finite number 22 | maxretries = 10 23 | 24 | ; How long do we wait on the manager port for an SSL session start? (ms) 25 | sslclienthellotimeout = 200 26 | 27 | ; Do we accept encrypted SSL manager connections? 28 | acceptencryptedconnection = yes 29 | 30 | ; Do we accept unencrypted manager connections? 31 | acceptunencryptedconnection = yes 32 | 33 | ; Amount of time to wait before timing out on writes to asterisk 34 | asteriskwritetimeout=100 35 | 36 | ; Amount of time to wait before timing out on writes to clients 37 | clientwritetimeout=200 38 | 39 | ; Our server-side SSL certificate; what we use when answering clients 40 | certfile = /var/lib/asterisk/certs/proxy-server.pem 41 | 42 | ; Address for proxy to listen on, can be set to * or x.x.x.x format 43 | ; recommend that you listen only on 127.0.0.1 or on an interface that 44 | ; is otherwise locked down to a trusted host, since the proxy 45 | ; currently provides NO authentication/authorization of its own 46 | listenaddress = * 47 | 48 | ; Port for proxy to listen on 49 | listenport = 1234 50 | 51 | ; Do we require authentication (either proxykey or astmanproxy.users entry)? 52 | ; See README and astmanproxy.users for more info 53 | authrequired = no 54 | 55 | ; Setting a proxy key requires proxy client connections to 56 | ; specify a ProxyKey: keyvalue header in the first incoming request 57 | ; to the proxy. Once this is done the client remains authenticated. 58 | ; This allows for a simple security layer. If not specified, 59 | ; no key is required, however other security measures (listening on 60 | ; a protected interface, behind firewall, iptables, etc) should be 61 | ; in place and well understood. 62 | ; proxykey = foobar 63 | 64 | ; local user and group for proxy to run as; will NOT run as root! 65 | proc_user = nobody 66 | proc_group = nobody 67 | 68 | ; default input and output format for clients 69 | ; inputformat = (standard|xml|http) 70 | ; outputformat = (standard|xml|csv) 71 | inputformat = standard 72 | outputformat = standard 73 | 74 | ; to enable REST/XMLRPC-like functionality, try this combo. 75 | ; this gives you http input (POST or GET) and either 76 | ; text/xml or text/plain output with NO webserver required! 77 | ; to access: http://[host]:1234/?Action=Ping&ActionID=Foo 78 | ; 79 | ; inputformat = http 80 | ; outputfomat = xml|standard 81 | ; autofilter = on 82 | 83 | ; set autofilter to be on or off by default 84 | ; with autofilter on, you can automatically filter responses 85 | ; to include only messages related to a specific actionid, 86 | ; as specified in a previous message 87 | ; valid values: on, off 88 | ; can also be changed on a per-client basis using 89 | ; ProxyAction: SetAutoFilter 90 | ; AutoFilter: (on|off) 91 | autofilter = off 92 | 93 | ; location of logfile -- will be owned by proc_user/proc_group 94 | ; /opt/log location is good on Mac OS X 95 | ;logfile = /opt/log/asterisk/astmanproxy.log 96 | logfile = /var/log/asterisk/astmanproxy.log 97 | 98 | -------------------------------------------------------------------------------- /configs/astmanproxy.users: -------------------------------------------------------------------------------- 1 | ; Astmanproxy user list 2 | ; 3 | ; Reload permissions by sending a SIGHUP 4 | ; 5 | ; "user" is the username, secret is the password, and the (optional) 6 | ; channel setting causes filtering of events only for the specified 7 | ; channel to be sent to this user. 8 | ; 9 | ; If multiple server connections are used, and the "server" parameter 10 | ; setting is given, the user will "prefer" the listed server when 11 | ; sending commands. It must be an exact match to the hostname/ip address 12 | ; in the .conf configuration file. 13 | ; 14 | ; user=secret,[channel],[out_context (to Asterisk)],[in_context (From Asterisk)],[accountcode],[server],[more_events] 15 | ; 16 | ; steve=steve,SIP/snom190,local, 17 | ; dave=securepass,SIP/1002,,,davesaccount,,y 18 | ; bill=pass 19 | -------------------------------------------------------------------------------- /configs/ssl.conf: -------------------------------------------------------------------------------- 1 | # Asterisk SSL configuration 2 | # 3 | # OpenSSL configuration file for custom Certificate Authority. Use a 4 | # different openssl.cnf file to generate certificate signing requests; 5 | # this one is for use only in Certificate Authority operations (csr -> 6 | # cert, cert revocation, revocation list generation). 7 | # 8 | # Be sure to customize this file prior to use, e.g. the commonName and 9 | # other options under the root_ca_distinguished_name section. 10 | 11 | HOME = . 12 | RANDFILE = $ENV::HOME/.rnd 13 | 14 | [ ca ] 15 | default_ca = MyAsteriskCA 16 | 17 | [ MyAsteriskCA ] 18 | dir = . 19 | # unsed at present, and my limited certs can be kept in current dir 20 | #certs = $dir/certs 21 | new_certs_dir = $dir/newcerts 22 | crl_dir = $dir/crl 23 | database = $dir/index 24 | 25 | certificate = $dir/ca-cert.pem 26 | serial = $dir/serial 27 | crl = $dir/ca-crl.pem 28 | private_key = $dir/private/ca-key.pem 29 | RANDFILE = $dir/private/.rand 30 | 31 | x509_extensions = usr_cert 32 | 33 | # Comment out the following two lines for the "traditional" 34 | # (and highly broken) format. 35 | name_opt = ca_default 36 | cert_opt = ca_default 37 | 38 | default_crl_days= 30 39 | default_days = 7300 40 | # if need to be compatible with older software, use weaker md5 41 | default_md = sha1 42 | # MSIE may need following set to yes? 43 | preserve = no 44 | 45 | # A few difference way of specifying how similar the request should look 46 | # For type CA, the listed attributes must be the same, and the optional 47 | # and supplied fields are just that :-) 48 | policy = policy_match 49 | 50 | # For the CA policy 51 | [ policy_match ] 52 | countryName = US 53 | stateOrProvinceName = CA 54 | organizationName = XYZ 55 | organizationalUnitName = XYZ 56 | commonName = asterisk 57 | emailAddress = root@localhost 58 | 59 | # For the 'anything' policy 60 | # At this point in time, you must list all acceptable 'object' 61 | # types. 62 | [ policy_anything ] 63 | countryName = optional 64 | stateOrProvinceName = optional 65 | localityName = optional 66 | organizationName = optional 67 | organizationalUnitName = optional 68 | commonName = supplied 69 | emailAddress = optional 70 | 71 | #################################################################### 72 | [ req ] 73 | default_bits = 2048 74 | default_keyfile = ./private/ca-key.pem 75 | default_md = sha1 76 | 77 | prompt = no 78 | distinguished_name = root_ca_distinguished_name 79 | 80 | x509_extensions = v3_ca 81 | 82 | # Passwords for private keys if not present they will be prompted for 83 | # input_password = secret 84 | # output_password = secret 85 | 86 | # This sets a mask for permitted string types. There are several options. 87 | # default: PrintableString, T61String, BMPString. 88 | # pkix : PrintableString, BMPString. 89 | # utf8only: only UTF8Strings. 90 | # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). 91 | # MASK:XXXX a literal mask value. 92 | # WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings 93 | # so use this option with caution! 94 | string_mask = nombstr 95 | 96 | # req_extensions = v3_req 97 | 98 | [ root_ca_distinguished_name ] 99 | commonName = NoSuchCA CA 100 | countryName = US 101 | stateOrProvinceName = California 102 | localityName = San Mateo 103 | 0.organizationName = domain.net 104 | emailAddress = nobody@localhost 105 | 106 | [ usr_cert ] 107 | 108 | # These extensions are added when 'ca' signs a request. 109 | 110 | # This goes against PKIX guidelines but some CAs do it and some software 111 | # requires this to avoid interpreting an end user certificate as a CA. 112 | 113 | basicConstraints=CA:FALSE 114 | 115 | # PKIX recommendations harmless if included in all certificates. 116 | subjectKeyIdentifier=hash 117 | authorityKeyIdentifier=keyid,issuer:always 118 | 119 | nsCaRevocationUrl = https://www.sial.org/ca-crl.pem 120 | #nsBaseUrl 121 | #nsRevocationUrl 122 | #nsRenewalUrl 123 | #nsCaPolicyUrl 124 | #nsSslServerName 125 | 126 | [ v3_req ] 127 | 128 | # Extensions to add to a certificate request 129 | 130 | basicConstraints = CA:FALSE 131 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment 132 | 133 | [ v3_ca ] 134 | 135 | 136 | # Extensions for a typical CA 137 | 138 | # PKIX recommendation. 139 | subjectKeyIdentifier=hash 140 | authorityKeyIdentifier=keyid:always,issuer:always 141 | 142 | # This is what PKIX recommends but some broken software chokes on critical 143 | # extensions. 144 | #basicConstraints = critical,CA:true 145 | # So we do this instead. 146 | basicConstraints = CA:true 147 | 148 | [ crl_ext ] 149 | 150 | # CRL extensions. 151 | # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. 152 | 153 | # issuerAltName=issuer:copy 154 | authorityKeyIdentifier=keyid:always,issuer:always 155 | -------------------------------------------------------------------------------- /debian/astmanproxy-default.ex: -------------------------------------------------------------------------------- 1 | # Defaults for astmanproxy initscript 2 | # sourced by /etc/init.d/astmanproxy 3 | # installed at /etc/default/astmanproxy by the maintainer scripts 4 | 5 | # 6 | # This is a POSIX shell fragment 7 | # 8 | 9 | # Additional options that are passed to the Daemon. 10 | DAEMON_OPTS="" 11 | -------------------------------------------------------------------------------- /debian/astmanproxy.1: -------------------------------------------------------------------------------- 1 | .\" Hey, EMACS: -*- nroff -*- 2 | .\" First parameter, NAME, should be all caps 3 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection 4 | .\" other parameters are allowed: see man(7), man(1) 5 | .TH ASTMANPROXY 1 "June 7, 2005" 6 | .\" Please adjust this date whenever revising the manpage. 7 | .\" 8 | .\" Some roff macros, for reference: 9 | .\" .nh disable hyphenation 10 | .\" .hy enable hyphenation 11 | .\" .ad l left justify 12 | .\" .ad b justify to both left and right margins 13 | .\" .nf disable filling 14 | .\" .fi enable filling 15 | .\" .br insert line break 16 | .\" .sp insert n+1 empty lines 17 | .\" for manpage-specific macros, see man(7) 18 | .SH NAME 19 | astmanproxy \- An asterisk manager interface proxy 20 | .SH SYNOPSIS 21 | .B astmanproxy 22 | .RI [ -d ] 23 | 24 | .B astmanproxy 25 | .RI -h 26 | 27 | .SH DESCRIPTION 28 | .B astmanproxy 29 | is a proxy between the manager interface of 30 | .B asterisk(8) 31 | and other clients. 32 | 33 | The program may do some \fBinteresting\fR \fIthings\fR, but someone else 34 | needs to write its man page (hint: very simple) 35 | 36 | .SH OPTIONS 37 | .B -d 38 | .RS 39 | Debug mode 40 | .RE 41 | 42 | .B -h 43 | .RS 44 | Display help message and exit. 45 | .RE 46 | 47 | .SH SEE ALSO 48 | .BR asterisk (8) 49 | 50 | any reference page? 51 | 52 | .SH AUTHOR 53 | astmanproxy was written by David C. Troy . 54 | 55 | This manual page was written by Tzafrir Cohen 56 | -------------------------------------------------------------------------------- /debian/astmanproxy.doc-base.EX: -------------------------------------------------------------------------------- 1 | Document: astmanproxy 2 | Title: Debian astmanproxy Manual 3 | Author: 4 | Abstract: This manual describes what astmanproxy is 5 | and how it can be used to 6 | manage online manuals on Debian systems. 7 | Section: unknown 8 | 9 | Format: debiandoc-sgml 10 | Files: /usr/share/doc/astmanproxy/astmanproxy.sgml.gz 11 | 12 | Format: postscript 13 | Files: /usr/share/doc/astmanproxy/astmanproxy.ps.gz 14 | 15 | Format: text 16 | Files: /usr/share/doc/astmanproxy/astmanproxy.text.gz 17 | 18 | Format: HTML 19 | Index: /usr/share/doc/astmanproxy/html/index.html 20 | Files: /usr/share/doc/astmanproxy/html/*.html 21 | 22 | 23 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | astmanproxy (1.21.90) unstable; urgency=low 2 | 3 | * Correcting the attribution in this file as it was never 4 | intended to be published. 5 | * Update code to very latest GIT based code with all 6 | contributions merged. 7 | * NOTE: This is effectively 1.22rc1 8 | 9 | -- Steve Davies Fri, 25 Jul 2008 14:34:15 +0100 10 | 11 | astmanproxy (1.21-4) unstable; urgency=low 12 | 13 | * Bug fix update to 1.21e 14 | 15 | -- Steve Davies Wed, 31 Mar 2008 12:32:14 +0100 16 | 17 | astmanproxy (1.21-3) unstable; urgency=low 18 | 19 | * Enhance and fix the filtering code to remember NewChannel events in 20 | case they are needed later 21 | 22 | -- Steve Davies Wed, 23 Jan 2008 16:51:56 +0000 23 | 24 | astmanproxy (1.21-2) unstable; urgency=low 25 | 26 | * Apply 1.21d patch to source tree 27 | 28 | -- Steve Davies Wed, 23 Jan 2008 16:51:56 +0000 29 | 30 | astmanproxy (1.21-1) unstable; urgency=low 31 | 32 | * Initial IPCortex build 33 | 34 | -- Steve Davies Tue, 11 Sep 2007 18:03:17 +0100 35 | 36 | astmanproxy (1.0-1) unstable; urgency=low 37 | 38 | * Initial Release. 39 | 40 | -- Tzafrir Cohen Tue, 7 Jun 2005 20:27:34 +0300 41 | 42 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 5 2 | -------------------------------------------------------------------------------- /debian/conffiles: -------------------------------------------------------------------------------- 1 | /etc/asterisk/astmanproxy.conf 2 | /etc/asterisk/astmanproxy.users 3 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: astmanproxy 2 | Section: unknown 3 | Priority: extra 4 | Maintainer: Steve Davies 5 | Build-Depends: debhelper (>= 5) 6 | Standards-Version: 3.7.2 7 | 8 | Package: astmanproxy 9 | Architecture: any 10 | Depends: ${shlibs:Depends}, ${misc:Depends} 11 | Description: Proxy daemon for Asterisk Manager Protocol 12 | Proxy daemon for Asterisk Manager Protocol. Its primary purpose is 13 | to offload the use of many threads to handle multiple AMP connections 14 | into a far more lightweight external daemon to improve stability and 15 | scaleability. It also adds authentication, filtering, and multi-server 16 | support. 17 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | This package was debianized by Tzafrir Cohen on 2 | Tue, 7 Jun 2005 20:27:34 +0300. 3 | 4 | It was downloaded from 5 | 6 | Copyright Holder: 7 | 8 | License: 9 | 10 | 11 | -------------------------------------------------------------------------------- /debian/dirs: -------------------------------------------------------------------------------- 1 | usr/bin 2 | usr/sbin 3 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README 2 | VERSIONS 3 | -------------------------------------------------------------------------------- /debian/init.d.ex: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # skeleton example file to build /etc/init.d/ scripts. 4 | # This file should be used to construct scripts for /etc/init.d. 5 | # 6 | # Written by Miquel van Smoorenburg . 7 | # Modified for Debian 8 | # by Ian Murdock . 9 | # 10 | # Version: @(#)skeleton 1.9 26-Feb-2001 miquels@cistron.nl 11 | # 12 | 13 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 14 | DAEMON=/usr/sbin/astmanproxy 15 | NAME=astmanproxy 16 | DESC=astmanproxy 17 | 18 | test -x $DAEMON || exit 0 19 | 20 | # Include astmanproxy defaults if available 21 | if [ -f /etc/default/astmanproxy ] ; then 22 | . /etc/default/astmanproxy 23 | fi 24 | 25 | set -e 26 | 27 | case "$1" in 28 | start) 29 | echo -n "Starting $DESC: " 30 | start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \ 31 | --exec $DAEMON -- $DAEMON_OPTS 32 | echo "$NAME." 33 | ;; 34 | stop) 35 | echo -n "Stopping $DESC: " 36 | start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \ 37 | --exec $DAEMON 38 | echo "$NAME." 39 | ;; 40 | #reload) 41 | # 42 | # If the daemon can reload its config files on the fly 43 | # for example by sending it SIGHUP, do it here. 44 | # 45 | # If the daemon responds to changes in its config file 46 | # directly anyway, make this a do-nothing entry. 47 | # 48 | # echo "Reloading $DESC configuration files." 49 | # start-stop-daemon --stop --signal 1 --quiet --pidfile \ 50 | # /var/run/$NAME.pid --exec $DAEMON 51 | #;; 52 | restart|force-reload) 53 | # 54 | # If the "reload" option is implemented, move the "force-reload" 55 | # option to the "reload" entry above. If not, "force-reload" is 56 | # just the same as "restart". 57 | # 58 | echo -n "Restarting $DESC: " 59 | start-stop-daemon --stop --quiet --pidfile \ 60 | /var/run/$NAME.pid --exec $DAEMON 61 | sleep 1 62 | start-stop-daemon --start --quiet --pidfile \ 63 | /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS 64 | echo "$NAME." 65 | ;; 66 | *) 67 | N=/etc/init.d/$NAME 68 | # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 69 | echo "Usage: $N {start|stop|restart|force-reload}" >&2 70 | exit 1 71 | ;; 72 | esac 73 | 74 | exit 0 75 | -------------------------------------------------------------------------------- /debian/postinst.ex: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # postinst script for astmanproxy 3 | # 4 | # see: dh_installdeb(1) 5 | 6 | set -e 7 | 8 | # summary of how this script can be called: 9 | # * `configure' 10 | # * `abort-upgrade' 11 | # * `abort-remove' `in-favour' 12 | # 13 | # * `abort-deconfigure' `in-favour' 14 | # `removing' 15 | # 16 | # for details, see http://www.debian.org/doc/debian-policy/ or 17 | # the debian-policy package 18 | # 19 | 20 | case "$1" in 21 | configure) 22 | 23 | ;; 24 | 25 | abort-upgrade|abort-remove|abort-deconfigure) 26 | 27 | ;; 28 | 29 | *) 30 | echo "postinst called with unknown argument \`$1'" >&2 31 | exit 1 32 | ;; 33 | esac 34 | 35 | # dh_installdeb will replace this with shell code automatically 36 | # generated by other debhelper scripts. 37 | 38 | #DEBHELPER# 39 | 40 | exit 0 41 | 42 | 43 | -------------------------------------------------------------------------------- /debian/postrm.ex: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # postrm script for astmanproxy 3 | # 4 | # see: dh_installdeb(1) 5 | 6 | set -e 7 | 8 | # summary of how this script can be called: 9 | # * `remove' 10 | # * `purge' 11 | # * `upgrade' 12 | # * `failed-upgrade' 13 | # * `abort-install' 14 | # * `abort-install' 15 | # * `abort-upgrade' 16 | # * `disappear' overwrit>r> 17 | # for details, see http://www.debian.org/doc/debian-policy/ or 18 | # the debian-policy package 19 | 20 | 21 | case "$1" in 22 | purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) 23 | 24 | 25 | ;; 26 | 27 | *) 28 | echo "postrm called with unknown argument \`$1'" >&2 29 | exit 1 30 | 31 | esac 32 | 33 | # dh_installdeb will replace this with shell code automatically 34 | # generated by other debhelper scripts. 35 | 36 | #DEBHELPER# 37 | 38 | exit 0 39 | -------------------------------------------------------------------------------- /debian/preinst.ex: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # preinst script for astmanproxy 3 | # 4 | # see: dh_installdeb(1) 5 | 6 | set -e 7 | 8 | # summary of how this script can be called: 9 | # * `install' 10 | # * `install' 11 | # * `upgrade' 12 | # * `abort-upgrade' 13 | # 14 | # for details, see http://www.debian.org/doc/debian-policy/ or 15 | # the debian-policy package 16 | 17 | 18 | case "$1" in 19 | install|upgrade) 20 | ;; 21 | 22 | abort-upgrade) 23 | ;; 24 | 25 | *) 26 | echo "preinst called with unknown argument \`$1'" >&2 27 | exit 1 28 | ;; 29 | esac 30 | 31 | # dh_installdeb will replace this with shell code automatically 32 | # generated by other debhelper scripts. 33 | 34 | #DEBHELPER# 35 | 36 | exit 0 37 | 38 | 39 | -------------------------------------------------------------------------------- /debian/prerm.ex: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # prerm script for astmanproxy 3 | # 4 | # see: dh_installdeb(1) 5 | 6 | set -e 7 | 8 | # summary of how this script can be called: 9 | # * `remove' 10 | # * `upgrade' 11 | # * `failed-upgrade' 12 | # * `remove' `in-favour' 13 | # * `deconfigure' `in-favour' 14 | # `removing' 15 | # 16 | # for details, see http://www.debian.org/doc/debian-policy/ or 17 | # the debian-policy package 18 | 19 | 20 | case "$1" in 21 | remove|upgrade|deconfigure) 22 | ;; 23 | failed-upgrade) 24 | ;; 25 | *) 26 | echo "prerm called with unknown argument \`$1'" >&2 27 | exit 1 28 | ;; 29 | esac 30 | 31 | # dh_installdeb will replace this with shell code automatically 32 | # generated by other debhelper scripts. 33 | 34 | #DEBHELPER# 35 | 36 | exit 0 37 | 38 | 39 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | # Sample debian/rules that uses debhelper. 4 | # This file was originally written by Joey Hess and Craig Small. 5 | # As a special exception, when this file is copied by dh-make into a 6 | # dh-make output file, you may use that output file without restriction. 7 | # This special exception was added by Craig Small in version 0.37 of dh-make. 8 | 9 | # Uncomment this to turn on verbose mode. 10 | #export DH_VERBOSE=1 11 | 12 | MAKE += PREFIX=/usr 13 | 14 | 15 | CFLAGS = -Wall -g 16 | 17 | ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) 18 | CFLAGS += -O0 19 | else 20 | CFLAGS += -O2 21 | endif 22 | 23 | configure: configure-stamp 24 | configure-stamp: 25 | dh_testdir 26 | # Add here commands to configure the package. 27 | 28 | touch configure-stamp 29 | 30 | 31 | build: build-stamp 32 | 33 | build-stamp: configure-stamp 34 | dh_testdir 35 | 36 | # Add here commands to compile the package. 37 | $(MAKE) 38 | #docbook-to-man debian/astmanproxy.sgml > astmanproxy.1 39 | 40 | touch build-stamp 41 | 42 | clean: 43 | dh_testdir 44 | dh_testroot 45 | rm -f build-stamp configure-stamp 46 | 47 | # Add here commands to clean up after the build process. 48 | -$(MAKE) clean 49 | 50 | dh_clean 51 | 52 | install: build 53 | dh_testdir 54 | dh_testroot 55 | dh_clean -k 56 | dh_installdirs 57 | 58 | # Add here commands to install the package into debian/astmanproxy. 59 | $(MAKE) install DESTDIR=$(CURDIR)/debian/astmanproxy 60 | 61 | 62 | # Build architecture-independent files here. 63 | binary-indep: build install 64 | # We have nothing to do by default. 65 | 66 | # Build architecture-dependent files here. 67 | binary-arch: build install 68 | dh_testdir 69 | dh_testroot 70 | dh_installchangelogs 71 | dh_installdocs 72 | dh_installexamples astmanproxy.conf 73 | # dh_install 74 | # dh_installmenu 75 | # dh_installdebconf 76 | # dh_installlogrotate 77 | # dh_installemacsen 78 | # dh_installpam 79 | # dh_installmime 80 | # dh_installinit 81 | # dh_installcron 82 | # dh_installinfo 83 | dh_installman debian/astmanproxy.1 84 | dh_link 85 | dh_strip 86 | dh_compress 87 | dh_fixperms 88 | # dh_perl 89 | # dh_python 90 | # dh_makeshlibs 91 | dh_installdeb 92 | dh_shlibdeps 93 | dh_gencontrol 94 | dh_md5sums 95 | dh_builddeb 96 | 97 | binary: binary-indep binary-arch 98 | .PHONY: build clean binary-indep binary-arch binary install configure 99 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | # Example watch control file for uscan 2 | # Rename this file to "watch" and then you can run the "uscan" command 3 | # to check for upstream updates and more. 4 | # See uscan(1) for format 5 | 6 | # Compulsory line, this is a version 2 file 7 | version=2 8 | 9 | http://www.popvox.com/astmanproxy/ astmanproxy-([0-9]*[0-9a-zA-Z.-]*).tgz 10 | # Uncomment to examine a Webpage 11 | # 12 | #http://www.example.com/downloads.php astmanproxy-(.*)\.tar\.gz 13 | 14 | # Uncomment to examine a Webserver directory 15 | #http://www.example.com/pub/astmanproxy-(.*)\.tar\.gz 16 | 17 | # Uncommment to examine a FTP server 18 | #ftp://ftp.example.com/pub/astmanproxy-(.*)\.tar\.gz debian uupdate 19 | -------------------------------------------------------------------------------- /doc/README.csv: -------------------------------------------------------------------------------- 1 | This I/O Handler was put together primarily as a demonstration of 2 | how easy it is to write handler routines for astmanproxy. 3 | 4 | However, it could be handy if you need CSV output, too. :) There 5 | is no _read routine at this time; I would welcome someone to write 6 | one and submit it to the project! 7 | 8 | -------------------------------------------------------------------------------- /doc/README.http: -------------------------------------------------------------------------------- 1 | This provides a very basic interface to http; it should be considered 2 | poorly built, lame, and dangerous until further notice. 3 | 4 | That said, if you GET or POST form data name/value pairs to the proxy port 5 | using HTTP, the proxy will treat it as Name: Value input and will act on 6 | it, and will respond back in whatever format you want to use. 7 | 8 | A useful scenario might be to use http input and xml output. 9 | 10 | The following form can be used as a test: 11 | 12 | 13 |
14 | Action:
15 | ActionID:
16 |
17 |
18 | 19 | 20 | See samples/httpast.html for a sample file that implements this. 21 | 22 | It would be best to use this with autofilter=on since right now you are 23 | only going to get one response block back, and it might as well be 24 | relevant. :) 25 | -------------------------------------------------------------------------------- /doc/README.standard: -------------------------------------------------------------------------------- 1 | This is the standard input/output handler. This handler implements the 2 | traditional Asterisk Manager interface I/O format, which can be 3 | described as follows: 4 | 5 | Each block of input and output should be described in terms of headers, 6 | each terminated with \r\n (cr+lf) and consisting of a name/value pair, 7 | and each block of headers should be terminated with two \r\n sequences: 8 | 9 | Name1: Value1\r\nName2: Value2\r\n\r\n\r\n 10 | 11 | The "standard" IO handler is used by astmanproxy to communicate with 12 | Asterisk server sessions. Clients connecting to astmanproxy can use 13 | the "standard" handler if they wish, for either input, output or both, 14 | or may use any other available handler. 15 | 16 | -------------------------------------------------------------------------------- /doc/README.xml: -------------------------------------------------------------------------------- 1 | The XML input handler is homegrown and very basic at this point. The 2 | following represents valid XML input: 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | It's entirely possible that other formats will break the parser entirely. 15 | I am toying with going with a lightweight XML parser like MiniXML that may 16 | give some more flexibility at the price of having a dependency. It is 17 | very likely, though, that the current parser will work fine for most 18 | applications. 19 | 20 | DCT 6/23/2005 21 | 22 | -------------------------------------------------------------------------------- /samples/httpast.html: -------------------------------------------------------------------------------- 1 | 2 |

Sample Astmanproxy HTTP Input

3 | Use form name/value pairs with GET or POST method to astmanproxy's
4 | HTTP input handler and XML output handler to get an instant
5 | Web interface to Asterisk! 6 |

7 |

8 | Server:
9 | Action:
10 | ActionID:
11 |
12 |
13 | 14 | -------------------------------------------------------------------------------- /samples/httpast2.html: -------------------------------------------------------------------------------- 1 | 2 |

Sample Astmanproxy HTTP Input

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

6 |

7 | Server:
8 | Action:
9 | ActionID:
10 |
11 |
12 | 13 | -------------------------------------------------------------------------------- /src/astmanproxy.c: -------------------------------------------------------------------------------- 1 | /* Asterisk Manager Proxy 2 | Copyright (c) 2005-2008 David C. Troy 3 | 4 | This program is free software, distributed under the terms of 5 | the GNU General Public License. 6 | 7 | astmanproxy.c 8 | contains the proxy server core, initialization, thread launching, 9 | loops, and exit routines 10 | */ 11 | 12 | #include "astmanproxy.h" 13 | 14 | extern int LoadHandlers( void ); 15 | extern void ReadConfig( void ); 16 | extern void ReadPerms( void ); 17 | extern FILE *OpenLogfile( void ); 18 | extern int SetProcUID( void ); 19 | 20 | extern void *proxyaction_do(char *proxyaction, struct message *m, struct mansession *s); 21 | extern void *ProxyLogin(struct mansession *s, struct message *m); 22 | extern void *ProxyLogoff(struct mansession *s); 23 | extern int ValidateAction(struct message *m, struct mansession *s, int inbound); 24 | extern int AddToStack(struct message *m, struct mansession *s, int withbody); 25 | extern void DelFromStack(struct message *m, struct mansession *s); 26 | extern void FreeStack(struct mansession *s); 27 | extern void ResendFromStack(char* uniqueid, struct mansession *s, struct message *m); 28 | 29 | int ConnectAsterisk(struct mansession *s); 30 | 31 | struct proxyconfig pc; 32 | struct mansession *sessions = NULL; 33 | struct iohandler *iohandlers = NULL; 34 | 35 | pthread_mutex_t sessionlock; 36 | pthread_mutex_t serverlock; 37 | pthread_mutex_t userslock; 38 | pthread_mutex_t loglock; 39 | pthread_mutex_t debuglock; 40 | static int asock = -1; 41 | FILE *proxylog; 42 | int debug = 0; 43 | 44 | void hup(int sig) { 45 | if (proxylog) { 46 | fflush(proxylog); 47 | fclose(proxylog); 48 | } 49 | proxylog = OpenLogfile(); 50 | logmsg("Received HUP -- reopened log"); 51 | ReadPerms(); 52 | logmsg("Received HUP -- reread permissions"); 53 | } 54 | 55 | void leave(int sig) { 56 | struct mansession *c; 57 | struct message sm, cm; 58 | struct iohandler *io; 59 | struct ast_server *srv; 60 | char iabuf[INET_ADDRSTRLEN]; 61 | 62 | /* Message to send to servers */ 63 | memset(&sm, 0, sizeof(struct message)); 64 | AddHeader(&sm, "Action: Logoff"); 65 | 66 | /* Message to send to clients */ 67 | memset(&cm, 0, sizeof(struct message)); 68 | AddHeader(&cm, PROXY_SHUTDOWN); 69 | 70 | if (debug) 71 | debugmsg("Notifying and closing sessions"); 72 | pthread_mutex_lock (&sessionlock); 73 | while (sessions) { 74 | c = sessions; 75 | sessions = sessions->next; 76 | 77 | if (c->server) { 78 | if (debug) 79 | debugmsg("asterisk@%s: closing session", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); 80 | c->output->write(c, &sm); 81 | logmsg("Shutdown, closed server %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); 82 | } else { 83 | if (debug) 84 | debugmsg("client@%s: closing session", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); 85 | c->output->write(c, &cm); 86 | logmsg("Shutdown, closed client %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); 87 | } 88 | close_sock(c->fd); /* close tcp & ssl socket */ 89 | FreeStack(c); 90 | pthread_mutex_destroy(&c->lock); 91 | free(c); 92 | } 93 | pthread_mutex_unlock (&sessionlock); 94 | 95 | /* unload server list */ 96 | while (pc.serverlist) { 97 | srv = pc.serverlist; 98 | pc.serverlist = srv->next; 99 | if (debug) 100 | debugmsg("asterisk@%s: forgetting", srv->ast_host); 101 | free(srv); 102 | } 103 | 104 | if (debug) 105 | debugmsg("Closing listener socket"); 106 | close_sock(asock); /* close tcp & ssl socket */ 107 | 108 | /* unload io handlers */ 109 | while (iohandlers) { 110 | io = iohandlers; 111 | iohandlers = iohandlers->next; 112 | if (debug) 113 | debugmsg("unloading: %s", io->formatname); 114 | dlclose(io->dlhandle); 115 | free(io); 116 | } 117 | 118 | if(debug) 119 | debugmsg("Done!\n"); 120 | logmsg("Proxy stopped; shutting down."); 121 | 122 | fclose(proxylog); 123 | pthread_mutex_destroy(&sessionlock); 124 | pthread_mutex_destroy(&loglock); 125 | pthread_mutex_destroy(&debuglock); 126 | exit(sig); 127 | } 128 | 129 | void Version( void ) 130 | { 131 | printf("astmanproxy: Version %s, (C) David C. Troy 2005-2008\n", PROXY_VERSION); 132 | return; 133 | } 134 | 135 | void Usage( void ) 136 | { 137 | printf("Usage: astmanproxy [-d|-h|-v]\n"); 138 | printf(" -d : Start in Debug Mode\n"); 139 | printf(" -h : Displays this message\n"); 140 | printf(" -v : Displays version information\n"); 141 | printf("Start with no options to run as daemon\n"); 142 | return; 143 | } 144 | 145 | void destroy_session(struct mansession *s) 146 | { 147 | struct mansession *cur, *prev = NULL; 148 | char iabuf[INET_ADDRSTRLEN]; 149 | 150 | pthread_mutex_lock(&sessionlock); 151 | cur = sessions; 152 | while(cur) { 153 | if (cur == s) 154 | break; 155 | prev = cur; 156 | cur = cur->next; 157 | } 158 | if (cur) { 159 | if (prev) 160 | prev->next = cur->next; 161 | else 162 | sessions = cur->next; 163 | debugmsg("Connection closed: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); 164 | close_sock(s->fd); /* close tcp/ssl socket */ 165 | FreeStack(s); 166 | pthread_mutex_destroy(&s->lock); 167 | free(s); 168 | } else if (debug) 169 | debugmsg("Trying to delete non-existent session %p?\n", s); 170 | pthread_mutex_unlock(&sessionlock); 171 | 172 | /* If there are no servers and no clients, why are we here? */ 173 | if (!sessions) { 174 | logmsg("Cannot connect to any servers! Leaving!"); 175 | leave(0); 176 | } 177 | } 178 | 179 | int WriteClients(struct message *m) { 180 | struct mansession *c; 181 | char *actionid; 182 | char *uniqueid; 183 | char *event; 184 | int valret; 185 | 186 | c = sessions; 187 | 188 | // We stash New Channel events in case they are filtered and need to be 189 | // re-played at a later time. Hangup events also clean the list 190 | // after being sent. 191 | event = astman_get_header(m, "Event"); 192 | if( !strcasecmp( event, "Newchannel" ) ) { 193 | AddToStack(m, m->session, 1); 194 | } 195 | while (c) { 196 | if ( !c->server && m->hdrcount>1 && (valret=ValidateAction(m, c, 1)) ) { 197 | // If VALRET > 1, then we may want to send a retrospective NewChannel before 198 | // writing out this event... 199 | // Send the retrospective Newchannel from the cache (m->session->cache) to this client (c)... 200 | if( (valret & ATS_SRCUNIQUE) && m->session ) { 201 | struct message m_temp; 202 | memset(&m_temp, 0, sizeof(struct message) ); 203 | uniqueid = astman_get_header(m, "SrcUniqueID"); 204 | ResendFromStack(uniqueid, m->session, &m_temp); 205 | m_temp.session = m->session; 206 | c->output->write(c, &m_temp); 207 | } 208 | if( (valret & ATS_DSTUNIQUE) && m->session ) { 209 | struct message m_temp; 210 | memset(&m_temp, 0, sizeof(struct message) ); 211 | uniqueid = astman_get_header(m, "DestUniqueID"); 212 | ResendFromStack(uniqueid, m->session, &m_temp); 213 | m_temp.session = m->session; 214 | c->output->write(c, &m_temp); 215 | } 216 | if (c->autofilter && c->actionid) { 217 | actionid = astman_get_header(m, ACTION_ID); 218 | if ( c->autofilter == 1 && !strcmp(actionid, c->actionid) ) 219 | // Original AutoFilter 220 | c->output->write(c, m); 221 | else if ( c->autofilter == 2 && *actionid == '\0' ) 222 | c->output->write(c, m); 223 | else if ( c->autofilter == 2 && !strncmp(actionid, c->actionid, strlen(c->actionid)) ) { 224 | // New AutoFilter, actionid like "ast123-XX" 225 | memmove( actionid, actionid+strlen(c->actionid), strlen(actionid)+1-strlen(c->actionid)); 226 | c->output->write(c, m); 227 | } else if (debug > 5) 228 | debugmsg("ActionID Filtered a message to a client\n"); 229 | } else 230 | c->output->write(c, m); 231 | 232 | if (c->inputcomplete) { 233 | pthread_mutex_lock(&c->lock); 234 | c->outputcomplete = 1; 235 | pthread_mutex_unlock(&c->lock); 236 | } 237 | } else if ( !c->server && m->hdrcount>1 && !valret && debug > 5) 238 | debugmsg("Validate Filtered a message to a client"); 239 | c = c->next; 240 | } 241 | if( !strcasecmp( event, "Hangup" ) ) { 242 | DelFromStack(m, m->session); 243 | } 244 | return 1; 245 | } 246 | 247 | int WriteAsterisk(struct message *m) { 248 | int i; 249 | char outstring[MAX_LEN], *dest; 250 | struct mansession *u, *s, *first; 251 | 252 | first = NULL; 253 | dest = NULL; 254 | 255 | s = sessions; 256 | u = m->session; 257 | 258 | if( u->user.server[0] != '\0' ) 259 | dest = u->user.server; 260 | else 261 | dest = astman_get_header(m, "Server"); 262 | 263 | if (debug && *dest) debugmsg("set destination: %s", dest); 264 | while ( s ) { 265 | if ( s->server && (s->connected > 0) ) { 266 | if ( !first ) 267 | first = s; 268 | if (*dest && !strcasecmp(dest, s->server->ast_host) ) 269 | break; 270 | } 271 | s = s->next; 272 | } 273 | 274 | if (!s) 275 | s = first; 276 | 277 | /* Check for no servers and empty block -- Don't pester Asterisk if it is one*/ 278 | if (!s || !s->server || (!m->hdrcount && !m->headers[0][0]) ) 279 | return 1; 280 | 281 | debugmsg("writing block to %s", s->server->ast_host); 282 | 283 | pthread_mutex_lock(&s->lock); 284 | for (i=0; ihdrcount; i++) { 285 | if (strcasecmp(m->headers[i], "Server:") ) { 286 | sprintf(outstring, "%s\r\n", m->headers[i]); 287 | ast_carefulwrite(s->fd, outstring, strlen(outstring), s->writetimeout ); 288 | } 289 | } 290 | ast_carefulwrite(s->fd, "\r\n", 2, s->writetimeout); 291 | pthread_mutex_unlock(&s->lock); 292 | return 1; 293 | } 294 | 295 | void *setactionid(char *actionid, struct message *m, struct mansession *s) 296 | { 297 | pthread_mutex_lock(&s->lock); 298 | if( s->autofilter < 2 ) { // Either save ActionID 299 | strncpy(s->actionid, actionid, MAX_LEN); 300 | } else if( strlen(s->actionid) + strlen(actionid) < MAX_LEN ) { // Or modify it 301 | memmove(actionid+strlen(s->actionid), actionid, strlen(actionid)+strlen(s->actionid)); 302 | strncpy(actionid, s->actionid, strlen(s->actionid)); 303 | } 304 | pthread_mutex_unlock(&s->lock); 305 | 306 | return 0; 307 | } 308 | 309 | /* Handles proxy client sessions; closely based on session_do from asterisk's manager.c */ 310 | void *session_do(struct mansession *s) 311 | { 312 | struct message m; 313 | int res; 314 | char *proxyaction, *actionid, *action, *key; 315 | 316 | if (s->input->onconnect) 317 | s->input->onconnect(s, &m); 318 | if (s->autofilter == 2) { 319 | pthread_mutex_lock(&s->lock); 320 | snprintf(s->actionid, MAX_LEN - 20, "amp%d-", s->fd); 321 | if (debug > 3) 322 | debugmsg("Setting actionID root to %s for new connection", s->actionid); 323 | pthread_mutex_unlock(&s->lock); 324 | } 325 | 326 | // Signal settings are not always inherited by threads, so ensure we ignore this one 327 | // as it is handled through error returns 328 | (void) signal(SIGPIPE, SIG_IGN); 329 | for (;;) { 330 | /* Get a complete message block from input handler */ 331 | memset( &m, 0, sizeof(struct message) ); 332 | if (debug > 3) 333 | debugmsg("calling %s_read...", s->input->formatname); 334 | res = s->input->read(s, &m); 335 | if (debug > 3) 336 | debugmsg("%s_read result = %d", s->input->formatname, res); 337 | m.session = s; 338 | 339 | if (res > 0) { 340 | /* Check for anything that requires proxy-side processing */ 341 | if (pc.key[0] != '\0' && !s->authenticated) { 342 | key = astman_get_header(&m, "ProxyKey"); 343 | if (!strcmp(key, pc.key) ) { 344 | pthread_mutex_lock(&s->lock); 345 | s->authenticated = 1; 346 | pthread_mutex_unlock(&s->lock); 347 | } else 348 | break; 349 | } 350 | 351 | proxyaction = astman_get_header(&m, "ProxyAction"); 352 | actionid = astman_get_header(&m, ACTION_ID); 353 | action = astman_get_header(&m, "Action"); 354 | if ( !strcasecmp(action, "Login") ) { 355 | s->authenticated = 0; 356 | ProxyLogin(s, &m); 357 | } else if ( !strcasecmp(action, "Logoff") ) 358 | ProxyLogoff(s); 359 | else if ( !strcasecmp(action, "Challenge") ) 360 | ProxyChallenge(s, &m); 361 | else if ( !(*proxyaction == '\0') ) 362 | proxyaction_do(proxyaction, &m, s); 363 | else if ( ValidateAction(&m, s, 0) ) { 364 | if ( !(*actionid == '\0') ) 365 | setactionid(actionid, &m, s); 366 | if ( !WriteAsterisk(&m) ) 367 | break; 368 | } else { 369 | SendError(s, "Action Filtered", actionid); 370 | } 371 | } else if (res < 0) 372 | break; 373 | } 374 | 375 | destroy_session(s); 376 | if (debug) 377 | debugmsg("--- exiting session_do thread ---"); 378 | pthread_exit(NULL); 379 | return NULL; 380 | } 381 | 382 | void *HandleAsterisk(struct mansession *s) 383 | { 384 | struct message *m; 385 | int res,i; 386 | char iabuf[INET_ADDRSTRLEN]; 387 | 388 | if (ConnectAsterisk(s)) 389 | goto leave; 390 | if (! (m = malloc(sizeof(struct message))) ) 391 | goto leave; 392 | 393 | // Signal settings are not always inherited by threads, so ensure we ignore this one 394 | (void) signal(SIGPIPE, SIG_IGN); 395 | for (;;) { 396 | if (debug) 397 | debugmsg("asterisk@%s: attempting read...", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); 398 | memset(m, 0, sizeof(struct message) ); 399 | res = s->input->read(s, m); 400 | m->session = s; 401 | 402 | if (res > 0) { 403 | if (debug) { 404 | for(i=0; ihdrcount; i++) { 405 | debugmsg("asterisk@%s got: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), m->headers[i]); 406 | } 407 | } 408 | 409 | if (!s->connected) { 410 | if ( !strcmp("Authentication accepted", astman_get_header(m, "Message")) ) { 411 | s->connected = 1; 412 | if (debug) 413 | debugmsg("asterisk@%s: connected successfully!", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr) ); 414 | } 415 | if ( !strcmp("Authentication failed", astman_get_header(m, "Message")) ) { 416 | s->connected = -1; 417 | } 418 | } 419 | 420 | m->session = s; 421 | AddHeader(m, "Server: %s", m->session->server->ast_host); 422 | 423 | if (!WriteClients(m)) 424 | break; 425 | } else if (res < 0) { 426 | /* TODO: do we need to do more than this here? or something different? */ 427 | if ( debug ) 428 | debugmsg("asterisk@%s: Not connected", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); 429 | if ( ConnectAsterisk(s) ) 430 | break; 431 | } 432 | } 433 | free(m); 434 | 435 | leave: 436 | if (debug) 437 | debugmsg("asterisk@%s: Giving up and exiting thread", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr) ); 438 | destroy_session(s); 439 | pthread_exit(NULL); 440 | return NULL; 441 | } 442 | 443 | int ConnectAsterisk(struct mansession *s) { 444 | char iabuf[INET_ADDRSTRLEN]; 445 | int r = 1, res = 0; 446 | struct message m; 447 | 448 | /* Don't try to do this if auth has already failed! */ 449 | if (s->connected < 0 ) 450 | return 1; 451 | else 452 | s->connected = 0; 453 | 454 | if (debug) 455 | debugmsg("asterisk@%s: Connecting (u=%s, p=%s, ssl=%s)", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), 456 | s->server->ast_user, s->server->ast_pass, s->server->use_ssl ? "on" : "off"); 457 | 458 | /* Construct auth message just once */ 459 | memset( &m, 0, sizeof(struct message) ); 460 | AddHeader(&m, "Action: Login"); 461 | AddHeader(&m, "Username: %s", s->server->ast_user); 462 | AddHeader(&m, "Secret: %s", s->server->ast_pass); 463 | AddHeader(&m, "Events: %s", s->server->ast_events); 464 | 465 | for ( ;; ) { 466 | if ( ast_connect(s) == -1 ) { 467 | if (debug) 468 | debugmsg("asterisk@%s: Connect failed, Retrying (%d) %s [%d]", 469 | ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), r, strerror(errno), errno ); 470 | if (pc.maxretries && (++r>pc.maxretries) ) { 471 | res = 1; 472 | break; 473 | } else 474 | sleep(pc.retryinterval); 475 | } else { 476 | /* Send login */ 477 | s->output->write(s, &m); 478 | res = 0; 479 | break; 480 | } 481 | } 482 | 483 | return res; 484 | } 485 | 486 | int StartServer(struct ast_server *srv) { 487 | 488 | struct mansession *s; 489 | struct hostent *ast_hostent; 490 | 491 | char iabuf[INET_ADDRSTRLEN]; 492 | pthread_attr_t attr; 493 | 494 | pthread_attr_init(&attr); 495 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 496 | 497 | ast_hostent = gethostbyname(srv->ast_host); 498 | if (!ast_hostent) { 499 | logmsg("Cannot resolve host %s, cannot add!", srv->ast_host); 500 | debugmsg("Cannot resolve host %s, cannot add!", srv->ast_host); 501 | return 1; 502 | } 503 | 504 | s = malloc(sizeof(struct mansession)); 505 | if ( !s ) { 506 | logmsg("Failed to allocate server session: %s\n", strerror(errno)); 507 | debugmsg("Failed to allocate server session: %s\n", strerror(errno)); 508 | return 1; 509 | } 510 | 511 | memset(s, 0, sizeof(struct mansession)); 512 | SetIOHandlers(s, "standard", "standard"); 513 | s->server = srv; 514 | 515 | bzero((char *) &s->sin,sizeof(s->sin)); 516 | s->sin.sin_family = AF_INET; 517 | memcpy( &s->sin.sin_addr.s_addr, ast_hostent->h_addr, ast_hostent->h_length ); 518 | s->sin.sin_port = htons(atoi(s->server->ast_port)); 519 | s->fd = socket(AF_INET, SOCK_STREAM, 0); 520 | 521 | pthread_mutex_lock(&sessionlock); 522 | s->next = sessions; 523 | sessions = s; 524 | pthread_mutex_unlock(&sessionlock); 525 | 526 | logmsg("Allocated Asterisk server session for %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); 527 | if (debug) { 528 | debugmsg("asterisk@%s: Allocated server session", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); 529 | debugmsg("Set %s input format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->input->formatname); 530 | debugmsg("Set %s output format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->output->formatname); 531 | } 532 | 533 | if (pthread_create(&s->t, &attr, (void *)HandleAsterisk, s)) 534 | destroy_session(s); 535 | else 536 | debugmsg("launched ast %s thread!", s->server->ast_host); 537 | 538 | pthread_attr_destroy(&attr); 539 | return 0; 540 | } 541 | 542 | int LaunchAsteriskThreads() { 543 | 544 | struct ast_server *srv; 545 | 546 | srv = pc.serverlist; 547 | while (srv) { 548 | StartServer(srv); 549 | srv = srv->next; 550 | } 551 | return 0; 552 | } 553 | 554 | int SetIOHandlers(struct mansession *s, char *ifmt, char *ofmt) 555 | { 556 | int res = 0; 557 | struct iohandler *io; 558 | 559 | io = iohandlers; 560 | pthread_mutex_lock(&s->lock); 561 | while (io) { 562 | if ( !strcasecmp(io->formatname, ifmt) ) 563 | s->input = io; 564 | 565 | if ( !strcasecmp(io->formatname, ofmt) ) 566 | s->output = io; 567 | 568 | io = io->next; 569 | } 570 | 571 | /* set default handlers if non match was found */ 572 | if (!s->output) { 573 | s->output = iohandlers; 574 | res = 1; 575 | } 576 | 577 | if (!s->input) { 578 | s->input = iohandlers; 579 | res = 1; 580 | } 581 | pthread_mutex_unlock(&s->lock); 582 | 583 | return res; 584 | } 585 | 586 | static void *accept_thread() 587 | { 588 | int as; 589 | struct sockaddr_in sin; 590 | socklen_t sinlen; 591 | struct mansession *s; 592 | struct protoent *p; 593 | int arg = 1; 594 | int flags; 595 | pthread_attr_t attr; 596 | char iabuf[INET_ADDRSTRLEN]; 597 | int is_encrypted; 598 | 599 | pthread_attr_init(&attr); 600 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 601 | 602 | for (;;) { 603 | sinlen = sizeof(sin); 604 | as = accept(asock, (struct sockaddr *)&sin, &sinlen); 605 | if (as < 0) { 606 | logmsg("Accept returned -1: %s\n", strerror(errno)); 607 | continue; 608 | } 609 | p = (struct protoent *)getprotobyname("tcp"); 610 | if( p ) { 611 | if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) { 612 | logmsg("Failed to set listener tcp connection to TCP_NODELAY mode: %s\n", strerror(errno)); 613 | } 614 | } 615 | 616 | /* SSL stuff below */ 617 | is_encrypted = is_encrypt_request(pc.sslclhellotimeout, as); 618 | debugmsg("is_encrypted: %d", is_encrypted); 619 | if (is_encrypted > 0) { 620 | if (!pc.acceptencryptedconnection) { 621 | if( debug ) 622 | debugmsg("Accepting encrypted connection disabled, closing the connection \n"); 623 | close_sock(as); 624 | continue; 625 | } else { 626 | if((as = saccept(as)) >= 0 ) { 627 | if( debug ) 628 | debugmsg("Can't accept the ssl connection, since SSL init has failed for certificate reason\n"); 629 | close_sock(as); 630 | continue; 631 | } 632 | } 633 | } else if (is_encrypted == -1) { 634 | logmsg("SSL version 2 is unsecure, we don't support it\n"); 635 | close_sock(as); 636 | continue; 637 | } 638 | if ( (! pc.acceptunencryptedconnection) && (as >= 0)) { 639 | logmsg("Unencrypted connections are not accepted and we received an unencrypted connection request\n"); 640 | close_sock(as); 641 | continue; 642 | } 643 | /* SSL stuff end */ 644 | 645 | s = malloc(sizeof(struct mansession)); 646 | if ( !s ) { 647 | logmsg("Failed to allocate listener session: %s\n", strerror(errno)); 648 | continue; 649 | } 650 | memset(s, 0, sizeof(struct mansession)); 651 | memcpy(&s->sin, &sin, sizeof(sin)); 652 | 653 | /* For safety, make sure socket is non-blocking */ 654 | flags = fcntl(get_real_fd(as), F_GETFL); 655 | fcntl(get_real_fd(as), F_SETFL, flags | O_NONBLOCK); 656 | 657 | pthread_mutex_init(&s->lock, NULL); 658 | s->fd = as; 659 | SetIOHandlers(s, pc.inputformat, pc.outputformat); 660 | s->autofilter = pc.autofilter; 661 | s->writetimeout = pc.clientwritetimeout; 662 | s->server = NULL; 663 | 664 | pthread_mutex_lock(&sessionlock); 665 | s->next = sessions; 666 | sessions = s; 667 | pthread_mutex_unlock(&sessionlock); 668 | 669 | logmsg("Connection received from %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); 670 | if (debug) { 671 | debugmsg("Connection received from %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr)); 672 | debugmsg("Set %s input format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->input->formatname); 673 | debugmsg("Set %s output format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->output->formatname); 674 | } 675 | 676 | if (pthread_create(&s->t, &attr, (void *)session_do, s)) 677 | destroy_session(s); 678 | } 679 | pthread_attr_destroy(&attr); 680 | return NULL; 681 | } 682 | 683 | int main(int argc, char *argv[]) 684 | { 685 | struct sockaddr_in serv_sock_addr, client_sock_addr; 686 | int cli_addrlen; 687 | struct linger lingerstruct; /* for socket reuse */ 688 | int flag; /* for socket reuse */ 689 | pid_t pid; 690 | char i; 691 | 692 | /* Figure out if we are in debug mode, handle other switches */ 693 | while (( i = getopt( argc, argv, "dhv" ) ) != EOF ) 694 | { 695 | switch( i ) { 696 | case 'd': 697 | debug++; 698 | break; 699 | case 'h': 700 | Usage(); 701 | exit(0); 702 | case 'v': 703 | Version(); 704 | exit(0); 705 | case '?': 706 | Usage(); 707 | exit(1); 708 | } 709 | } 710 | 711 | 712 | ReadConfig(); 713 | proxylog = OpenLogfile(); 714 | debugmsg("loading handlers"); 715 | LoadHandlers(); 716 | debugmsg("loaded handlers"); 717 | 718 | if (SetProcUID()) { 719 | fprintf(stderr,"Cannot set user/group! Check proc_user and proc_group config setting!\n"); 720 | exit(1); 721 | } 722 | 723 | /* If we are not in debug mode, then fork to background */ 724 | if (!debug) { 725 | if ( (pid = fork()) < 0) 726 | exit( 1 ); 727 | else if ( pid > 0) 728 | exit( 0 ); 729 | } 730 | 731 | /* Setup signal handlers */ 732 | (void) signal(SIGINT,leave); 733 | (void) signal(SIGHUP,hup); 734 | (void) signal(SIGTERM,leave); 735 | (void) signal(SIGPIPE, SIG_IGN); 736 | 737 | /* Initialize global mutexes */ 738 | pthread_mutex_init(&sessionlock, NULL); 739 | pthread_mutex_init(&userslock, NULL); 740 | pthread_mutex_init(&loglock, NULL); 741 | pthread_mutex_init(&debuglock, NULL); 742 | 743 | /* Read initial state for user permissions */ 744 | ReadPerms(); 745 | 746 | /* Initialize SSL Client-Side Context */ 747 | client_init_secure(); 748 | 749 | /* Initialize global client/server list */ 750 | sessions = NULL; 751 | LaunchAsteriskThreads(); 752 | 753 | /* Setup listener socket to setup new sessions... */ 754 | if ((asock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 755 | fprintf(stderr,"Cannot create listener socket!\n"); 756 | exit(1); 757 | } 758 | bzero((char *) &serv_sock_addr, sizeof serv_sock_addr ); 759 | serv_sock_addr.sin_family = AF_INET; 760 | 761 | if ( !strcmp(pc.listen_addr,"*") ) 762 | serv_sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); 763 | else 764 | serv_sock_addr.sin_addr.s_addr = inet_addr( pc.listen_addr); 765 | 766 | serv_sock_addr.sin_port = htons((short)pc.listen_port); 767 | 768 | /* Set listener socket re-use options */ 769 | setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag)); 770 | lingerstruct.l_onoff = 1; 771 | lingerstruct.l_linger = 5; 772 | setsockopt(asock, SOL_SOCKET, SO_LINGER, (void *)&lingerstruct, sizeof(lingerstruct)); 773 | 774 | if (bind(asock, (struct sockaddr *) &serv_sock_addr, sizeof serv_sock_addr ) < 0) { 775 | fprintf(stderr,"Cannot bind to listener socket!\n"); 776 | exit(1); 777 | } 778 | 779 | listen(asock, 5); 780 | cli_addrlen = sizeof(client_sock_addr); 781 | if (debug) 782 | debugmsg("Listening for connections"); 783 | logmsg("Proxy Started: Listening for connections"); 784 | 785 | /* Launch listener thread */ 786 | accept_thread(); 787 | 788 | pthread_exit(NULL); 789 | exit(0); 790 | } 791 | -------------------------------------------------------------------------------- /src/common.c: -------------------------------------------------------------------------------- 1 | /* Asterisk Manager Proxy 2 | Copyright (c) 2005-2008 David C. Troy 3 | 4 | This program is free software, distributed under the terms of 5 | the GNU General Public License. 6 | 7 | common.c 8 | contains common utililty functions used by both astmanproxy 9 | core as well as (many) of the various I/O handlers 10 | */ 11 | 12 | #include "astmanproxy.h" 13 | 14 | /* This routine based on get_input from Asterisk manager.c */ 15 | /* Good generic line-based input routine for \r\n\r\n terminated input */ 16 | /* Used by standard.c and other input handlers */ 17 | int get_input(struct mansession *s, char *output) 18 | { 19 | /* output must have at least sizeof(s->inbuf) space */ 20 | int res; 21 | int x; 22 | struct pollfd fds[1]; 23 | char iabuf[INET_ADDRSTRLEN]; 24 | 25 | /* Look for \r\n from the front, our preferred end of line */ 26 | for (x=0;xinlen;x++) { 27 | int xtra = 0; 28 | if (s->inbuf[x] == '\n') { 29 | if (x && s->inbuf[x-1] == '\r') { 30 | xtra = 1; 31 | } 32 | /* Copy output data not including \r\n */ 33 | memcpy(output, s->inbuf, x - xtra); 34 | /* Add trailing \0 */ 35 | output[x-xtra] = '\0'; 36 | /* Move remaining data back to the front */ 37 | memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x); 38 | s->inlen -= (x + 1); 39 | return 1; 40 | } 41 | } 42 | 43 | if (s->inlen >= sizeof(s->inbuf) - 1) { 44 | if (debug) 45 | debugmsg("Warning: Got long line with no end from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf); 46 | s->inlen = 0; 47 | } 48 | /* get actual fd, even if a negative SSL fd */ 49 | fds[0].fd = get_real_fd(s->fd); 50 | 51 | fds[0].events = POLLIN; 52 | do { 53 | res = poll(fds, 1, -1); 54 | if (res < 0) { 55 | if (errno == EINTR) { 56 | if (s->dead) 57 | return -1; 58 | continue; 59 | } 60 | if (debug) 61 | debugmsg("Select returned error"); 62 | return -1; 63 | } else if (res > 0) { 64 | pthread_mutex_lock(&s->lock); 65 | /* read from socket; SSL or otherwise */ 66 | res = m_recv(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen, 0); 67 | pthread_mutex_unlock(&s->lock); 68 | if (res < 1) 69 | return -1; 70 | break; 71 | 72 | } 73 | } while(1); 74 | 75 | /* We have some input, but it's not ready for processing */ 76 | s->inlen += res; 77 | s->inbuf[s->inlen] = '\0'; 78 | return 0; 79 | } 80 | 81 | char *astman_get_header(struct message *m, char *var) 82 | { 83 | char cmp[80]; 84 | int x; 85 | snprintf(cmp, sizeof(cmp), "%s: ", var); 86 | for (x=0;xhdrcount;x++) 87 | if (!strncasecmp(cmp, m->headers[x], strlen(cmp))) 88 | return m->headers[x] + strlen(cmp); 89 | return ""; 90 | } 91 | 92 | int AddHeader(struct message *m, const char *fmt, ...) { 93 | va_list ap; 94 | 95 | int res; 96 | 97 | if (m->hdrcount < MAX_HEADERS - 1) { 98 | va_start(ap, fmt); 99 | vsprintf(m->headers[m->hdrcount], fmt, ap); 100 | va_end(ap); 101 | m->hdrcount++; 102 | res = 0; 103 | } else 104 | res = 1; 105 | 106 | return res; 107 | } 108 | 109 | /* Recursive thread safe replacement of inet_ntoa */ 110 | const char *ast_inet_ntoa(char *buf, int bufsiz, struct in_addr ia) 111 | { 112 | return inet_ntop(AF_INET, &ia, buf, bufsiz); 113 | } 114 | 115 | 116 | /*! If you are calling ast_carefulwrite, it is assumed that you are calling 117 | it on a file descriptor that _DOES_ have NONBLOCK set. This way, 118 | there is only one system call made to do a write, unless we actually 119 | have a need to wait. This way, we get better performance. */ 120 | int ast_carefulwrite(int fd, char *s, int len, int timeoutms) 121 | { 122 | /* Try to write string, but wait no more than ms milliseconds 123 | before timing out */ 124 | int res=0; 125 | struct pollfd fds[1]; 126 | while(len) { 127 | res = m_send(fd, s, len); 128 | if ((res < 0) && (errno != EAGAIN)) { 129 | return -1; 130 | } 131 | if (res < 0) res = 0; 132 | len -= res; 133 | s += res; 134 | res = 0; 135 | if (len) { 136 | fds[0].fd = get_real_fd(fd); 137 | fds[0].events = POLLOUT; 138 | /* Wait until writable again */ 139 | res = poll(fds, 1, timeoutms); 140 | if (res < 1) 141 | return -1; 142 | } 143 | } 144 | return res; 145 | } 146 | 147 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | /* Asterisk Manager Proxy 2 | Copyright (c) 2005-2008 David C. Troy 3 | 4 | This program is free software, distributed under the terms of 5 | the GNU General Public License. 6 | 7 | config.c 8 | routines to read and parse the configuration file and initialize 9 | the internal configuration datastructures 10 | */ 11 | 12 | #include 13 | #include 14 | #include "astmanproxy.h" 15 | 16 | extern struct iohandler *iohandlers; 17 | 18 | void *add_server(char *srvspec) { 19 | 20 | int ccount = 0; 21 | struct ast_server *srv; 22 | char *s; 23 | char usessl[10]; 24 | 25 | /* malloc ourselves a server credentials structure */ 26 | srv = malloc(sizeof(struct ast_server)); 27 | if ( !srv ) { 28 | fprintf(stderr, "Failed to allocate server credentials: %s\n", strerror(errno)); 29 | exit(1); 30 | } 31 | memset(srv, 0, sizeof (struct ast_server) ); 32 | memset(usessl, 0, sizeof (usessl) ); 33 | 34 | s = srvspec; 35 | do { 36 | *s = tolower(*s); 37 | if ( *s == ',' ) { 38 | ccount++; 39 | continue; 40 | } 41 | switch(ccount) { 42 | case 0: 43 | strncat(srv->ast_host, s, 1); 44 | break; 45 | case 1: 46 | strncat(srv->ast_port, s, 1); 47 | break; 48 | case 2: 49 | strncat(srv->ast_user, s, 1); 50 | break; 51 | case 3: 52 | strncat(srv->ast_pass, s, 1); 53 | break; 54 | case 4: 55 | strncat(srv->ast_events, s, 1); 56 | break; 57 | case 5: 58 | strncat(usessl, s, 1); 59 | break; 60 | } 61 | } while (*(s++)); 62 | 63 | 64 | if (!*srv->ast_host || !*srv->ast_port || !*srv->ast_user || !*srv->ast_pass || !*srv->ast_events || !*usessl) { 65 | fprintf(stderr, "Aborting: server spec incomplete: %s\n", srvspec); 66 | free(srv); 67 | exit(1); 68 | } 69 | 70 | srv->use_ssl = (!strcmp(usessl,"on")); 71 | srv->next = pc.serverlist; 72 | pc.serverlist = srv; 73 | 74 | return 0; 75 | } 76 | 77 | void *processline(char *s) { 78 | char name[80],value[80]; 79 | int nvstate = 0; 80 | 81 | 82 | memset (name,0,sizeof name); 83 | memset (value,0,sizeof value); 84 | 85 | do { 86 | *s = tolower(*s); 87 | 88 | if ( *s == ' ' || *s == '\t') 89 | continue; 90 | if ( *s == ';' || *s == '#' || *s == '\r' || *s == '\n' ) 91 | break; 92 | if ( *s == '=' ) { 93 | nvstate = 1; 94 | continue; 95 | } 96 | if (!nvstate) 97 | strncat(name, s, 1); 98 | else 99 | strncat(value, s, 1); 100 | } while (*(s++)); 101 | 102 | if (debug) 103 | debugmsg("config: %s, %s", name, value); 104 | 105 | if ( !strcmp(name,"host") ) 106 | add_server(value); 107 | else if (!strcmp(name,"retryinterval") ) 108 | pc.retryinterval = atoi(value); 109 | else if (!strcmp(name,"maxretries") ) 110 | pc.maxretries = atoi(value); 111 | else if (!strcmp(name,"listenaddress") ) 112 | strcpy(pc.listen_addr, value); 113 | else if (!strcmp(name,"listenport") ) 114 | pc.listen_port = atoi(value); 115 | else if (!strcmp(name,"asteriskwritetimeout") ) 116 | pc.asteriskwritetimeout = atoi(value); 117 | else if (!strcmp(name,"clientwritetimeout") ) 118 | pc.clientwritetimeout = atoi(value); 119 | else if (!strcmp(name,"sslclienthellotimeout") ) 120 | pc.sslclhellotimeout = atoi(value); 121 | else if (!strcmp(name,"authrequired") ) 122 | pc.authrequired = strcmp(value,"yes") ? 0 : 1; 123 | else if (!strcmp(name,"acceptencryptedconnection") ) 124 | pc.acceptencryptedconnection = strcmp(value,"yes") ? 0 : 1; 125 | else if (!strcmp(name,"acceptunencryptedconnection") ) 126 | pc.acceptunencryptedconnection = strcmp(value,"yes") ? 0 : 1; 127 | else if (!strcmp(name,"certfile") ) 128 | strcpy(pc.certfile, value); 129 | else if (!strcmp(name,"proxykey") ) 130 | strcpy(pc.key, value); 131 | else if (!strcmp(name,"proc_user") ) 132 | strcpy(pc.proc_user, value); 133 | else if (!strcmp(name,"proc_group") ) 134 | strcpy(pc.proc_group, value); 135 | else if (!strcmp(name,"logfile") ) 136 | strcpy(pc.logfile, value); 137 | else if (!strcmp(name,"autofilter") ) { 138 | if( ! strcmp(value,"on") ) 139 | pc.autofilter = 1; 140 | else if( ! strcmp(value,"unique") ) 141 | pc.autofilter = 2; 142 | else 143 | pc.autofilter = 0; 144 | } else if (!strcmp(name,"outputformat") ) 145 | strcpy(pc.outputformat, value); 146 | else if (!strcmp(name,"inputformat") ) 147 | strcpy(pc.inputformat, value); 148 | 149 | return 0; 150 | } 151 | 152 | int LoadHandlers() { 153 | 154 | void *dlhandle = NULL; 155 | const char *error; 156 | char fmt[20], moddir[80] = MDIR, modfile[80]; 157 | DIR *mods; 158 | struct dirent *d; 159 | void *rh, *wh, *och; 160 | struct iohandler *io = NULL; 161 | 162 | mods = opendir(moddir); 163 | if (!mods) 164 | exit(1); 165 | 166 | while((d = readdir(mods))) { 167 | /* Must end in .so to load it. */ 168 | if ( (strlen(d->d_name) > 3) && !strcasecmp(d->d_name + strlen(d->d_name) - 3, ".so") ) { 169 | 170 | memset(fmt, 0, sizeof fmt); 171 | strncpy(fmt, d->d_name, strlen(d->d_name) - 3); 172 | 173 | sprintf(modfile, "%s/%s", moddir, d->d_name); 174 | if (debug) 175 | debugmsg("loading: module %s (%s)", fmt, modfile); 176 | 177 | dlhandle = dlopen (modfile, RTLD_LAZY); 178 | if (!dlhandle) { 179 | fprintf(stderr, "dlopen failed: %s\n", dlerror()); 180 | exit(1); 181 | } 182 | 183 | rh = dlsym(dlhandle, "_read"); 184 | if ((error = dlerror()) != NULL) { 185 | if (debug) 186 | debugmsg("loading: note, %s_read does not exist; ignoring", fmt); 187 | } 188 | 189 | wh = dlsym(dlhandle, "_write"); 190 | if ((error = dlerror()) != NULL) { 191 | if (debug) 192 | debugmsg("loading: note, %s_write does not exist; ignoring", fmt); 193 | } 194 | 195 | och = dlsym(dlhandle, "_onconnect"); 196 | if ((error = dlerror()) != NULL) { 197 | if (debug) 198 | debugmsg("loading: note, %s_onconnect does not exist; ignoring", fmt); 199 | } 200 | 201 | if (rh || wh) { 202 | io = malloc(sizeof(struct iohandler)); 203 | memset(io, 0, sizeof(struct iohandler)); 204 | strcpy(io->formatname, fmt); 205 | if (rh) 206 | io->read = rh; 207 | if (wh) 208 | io->write = wh; 209 | if (och) 210 | io->onconnect = och; 211 | 212 | io->dlhandle = dlhandle; 213 | io->next = iohandlers; 214 | iohandlers = io; 215 | } else 216 | dlclose(dlhandle); 217 | } 218 | } 219 | closedir(mods); 220 | 221 | if (!iohandlers) { 222 | fprintf(stderr, "Unable to load *ANY* IO Handlers from %s!\n", MDIR); 223 | exit(1); 224 | } 225 | 226 | return 0; 227 | } 228 | 229 | 230 | int ReadConfig() { 231 | FILE *FP; 232 | char buf[1024]; 233 | char cfn[80]; 234 | 235 | 236 | memset( &pc, 0, sizeof pc ); 237 | 238 | /* Set nonzero config defaults */ 239 | pc.asteriskwritetimeout = 100; 240 | pc.clientwritetimeout = 100; 241 | pc.sslclhellotimeout = 500; 242 | 243 | sprintf(cfn, "%s/%s", CDIR, CFILE); 244 | FP = fopen( cfn, "r" ); 245 | 246 | if ( !FP ) { 247 | fprintf(stderr, "Unable to open config file: %s/%s!\n", CDIR, CFILE); 248 | exit( 1 ); 249 | } 250 | 251 | if (debug) 252 | debugmsg("config: parsing configuration file: %s", cfn); 253 | 254 | while ( fgets( buf, sizeof buf, FP ) ) { 255 | if (*buf == ';' || *buf == '\r' || *buf == '\n' || *buf == '#') continue; 256 | processline(buf); 257 | } 258 | 259 | fclose(FP); 260 | 261 | /* initialize SSL layer with our server certfile */ 262 | init_secure(pc.certfile); 263 | 264 | return 0; 265 | } 266 | 267 | FILE *OpenLogfile() { 268 | FILE *FP; 269 | FP = fopen( pc.logfile, "a" ); 270 | if ( !FP ) { 271 | fprintf(stderr, "Unable to open logfile: %s!\n", pc.logfile); 272 | exit( 1 ); 273 | } 274 | 275 | return FP; 276 | } 277 | 278 | int SetProcUID() { 279 | 280 | struct passwd *pwent; 281 | struct group *gp; 282 | uid_t newuid = 0; 283 | gid_t newgid = 0; 284 | 285 | if ((pwent = (struct passwd *)getpwnam( pc.proc_user )) == NULL) { 286 | fprintf(stderr, "getpwnam(%s) failed.\n", pc.proc_user); 287 | return(-1); 288 | } else 289 | newuid = pwent->pw_uid; 290 | 291 | if ( newuid == 0 ) { 292 | fprintf(stderr, "getpwnam(%s) returned root user; aborting!\n", pc.proc_user); 293 | return(-1); 294 | } 295 | 296 | if ((gp = (struct group *)getgrnam( pc.proc_group )) == NULL) { 297 | fprintf(stderr, "getgrnam(%s) failed.\n", pc.proc_group); 298 | return(-1); 299 | } else 300 | newgid = gp->gr_gid; 301 | 302 | if ( chown( pc.logfile, newuid, newgid ) < 0 ) { 303 | fprintf(stderr, "chown(%d,%d) of %s failed!\n", newuid, newgid, pc.logfile); 304 | return( -1 ); 305 | } 306 | 307 | if (setgid(newgid) < 0) { 308 | fprintf(stderr, "setgid(%d) failed.\n", newgid); 309 | return(-1); 310 | } 311 | 312 | if (setuid(newuid) < 0) { 313 | fprintf(stderr, "setuid(%d) failed.\n", newuid); 314 | return(-1); 315 | } 316 | 317 | return 0; 318 | } 319 | -------------------------------------------------------------------------------- /src/config_perms.c: -------------------------------------------------------------------------------- 1 | /* Asterisk Manager Proxy 2 | Copyright (c) 2005-2008 David C. Troy 3 | 4 | This program is free software, distributed under the terms of 5 | the GNU General Public License. 6 | 7 | config_perms.c 8 | routines to read and parse the astmanproxy.users file 9 | */ 10 | 11 | #include "astmanproxy.h" 12 | 13 | extern pthread_mutex_t userslock; 14 | 15 | void *free_userperm(struct proxy_user *pu) { 16 | struct proxy_user *next_pu; 17 | 18 | while( pu ) { 19 | next_pu = pu->next; 20 | free( pu ); 21 | pu = next_pu; 22 | } 23 | return 0; 24 | } 25 | 26 | void *add_userperm(char* username, char *userspec, struct proxy_user **pu) { 27 | 28 | int ccount = 0; 29 | struct proxy_user *user; 30 | char *s; 31 | 32 | /* malloc ourselves a server credentials structure */ 33 | user = malloc(sizeof(struct proxy_user)); 34 | if ( !user ) { 35 | fprintf(stderr, "Failed to allocate user credentials: %s\n", strerror(errno)); 36 | exit(1); 37 | } 38 | memset(user, 0, sizeof (struct proxy_user) ); 39 | 40 | s = userspec; 41 | strncpy(user->username, username, sizeof(user->username)-1 ); 42 | do { 43 | if ( *s == ',' ) { 44 | ccount++; 45 | continue; 46 | } 47 | if( ccount > 0 ) 48 | *s = tolower(*s); 49 | switch(ccount) { 50 | case 0: 51 | strncat(user->secret, s, 1); 52 | break; 53 | case 1: 54 | strncat(user->channel, s, 1); 55 | break; 56 | case 2: 57 | strncat(user->ocontext, s, 1); 58 | break; 59 | case 3: 60 | strncat(user->icontext, s, 1); 61 | break; 62 | case 4: 63 | strncat(user->account, s, 1); 64 | break; 65 | case 5: 66 | strncat(user->server, s, 1); 67 | break; 68 | case 6: 69 | user->more_events[0] = 'y'; // Any non-null entry 70 | break; 71 | } 72 | } while (*(s++)); 73 | 74 | user->next = *pu; 75 | *pu = user; 76 | 77 | return 0; 78 | } 79 | 80 | void *processperm(char *s, struct proxy_user **pu) { 81 | char name[80],value[80]; 82 | int nvstate = 0; 83 | 84 | 85 | memset (name,0,sizeof name); 86 | memset (value,0,sizeof value); 87 | 88 | do { 89 | if ( *s == ' ' || *s == '\t') 90 | continue; 91 | if ( *s == ';' || *s == '#' || *s == '\r' || *s == '\n' ) 92 | break; 93 | if ( *s == '=' ) { 94 | nvstate = 1; 95 | continue; 96 | } 97 | if (!nvstate) 98 | strncat(name, s, 1); 99 | else 100 | strncat(value, s, 1); 101 | } while (*(s++)); 102 | 103 | if (debug) 104 | debugmsg("perm: %s, %s", name, value); 105 | 106 | add_userperm(name,value,pu); 107 | 108 | return 0; 109 | } 110 | 111 | int ReadPerms() { 112 | FILE *FP; 113 | char buf[1024]; 114 | char cfn[80]; 115 | struct proxy_user *pu; 116 | 117 | pu=0; 118 | sprintf(cfn, "%s/%s", PDIR, PFILE); 119 | FP = fopen( cfn, "r" ); 120 | 121 | if ( !FP ) 122 | { 123 | fprintf(stderr, "Unable to open permissions file: %s/%s!\n", PDIR, PFILE); 124 | exit( 1 ); 125 | } 126 | 127 | if (debug) 128 | debugmsg("config: parsing configuration file: %s", cfn); 129 | 130 | while ( fgets( buf, sizeof buf, FP ) ) { 131 | if (*buf == ';' || *buf == '\r' || *buf == '\n' || *buf == '#') continue; 132 | processperm(buf,&pu); 133 | } 134 | 135 | fclose(FP); 136 | 137 | pthread_mutex_lock(&userslock); 138 | free_userperm(pc.userlist); 139 | pc.userlist=pu; 140 | pthread_mutex_unlock(&userslock); 141 | 142 | return 0; 143 | } 144 | 145 | -------------------------------------------------------------------------------- /src/csv.c: -------------------------------------------------------------------------------- 1 | /* Asterisk Manager Proxy 2 | Copyright (c) 2005-2008 David C. Troy 3 | 4 | This program is free software, distributed under the terms of 5 | the GNU General Public License. 6 | 7 | csv.c 8 | CSV I/O Handler 9 | */ 10 | 11 | #include "astmanproxy.h" 12 | 13 | /* TODO: catch and expand/handle commas in output */ 14 | 15 | int _write(struct mansession *s, struct message *m) { 16 | int i; 17 | char outstring[MAX_LEN]; 18 | 19 | pthread_mutex_lock(&s->lock); 20 | for (i=0; ihdrcount; i++) { 21 | sprintf(outstring, "\"%s\"", m->headers[i]); 22 | if (ihdrcount-1) 23 | strcat(outstring, ", "); 24 | ast_carefulwrite(s->fd, outstring, strlen(outstring), s->writetimeout); 25 | } 26 | ast_carefulwrite(s->fd, "\r\n\r\n", 4, s->writetimeout); 27 | pthread_mutex_unlock(&s->lock); 28 | 29 | return 0; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/dlfcn.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2002 Jorge Acereda & 3 | Peter O'Gorman 4 | 5 | Portions may be copyright others, see the AUTHORS file included with this 6 | distribution. 7 | 8 | Maintained by Peter O'Gorman 9 | 10 | Bug Reports and other queries should go to 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | /* Just playing to see if it would compile with the freebsd headers, it does, 44 | * but because of the different values for RTLD_LOCAL etc, it would break binary 45 | * compat... oh well 46 | */ 47 | #ifndef __BSD_VISIBLE 48 | #define __BSD_VISIBLE 1 49 | #endif 50 | #include "dlfcn-compat.h" 51 | 52 | #ifndef dl_restrict 53 | #define dl_restrict __restrict 54 | #endif 55 | /* This is not available on 10.1 */ 56 | #ifndef LC_LOAD_WEAK_DYLIB 57 | #define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD) 58 | #endif 59 | 60 | /* With this stuff here, this thing may actually compile/run on 10.0 systems 61 | * Not that I have a 10.0 system to test it on anylonger 62 | */ 63 | #ifndef LC_REQ_DYLD 64 | #define LC_REQ_DYLD 0x80000000 65 | #endif 66 | #ifndef NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 67 | #define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4 68 | #endif 69 | #ifndef NSADDIMAGE_OPTION_RETURN_ON_ERROR 70 | #define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 71 | #endif 72 | #ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 73 | #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0 74 | #endif 75 | #ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 76 | #define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4 77 | #endif 78 | /* These symbols will be looked for in dyld */ 79 | static const struct mach_header *(*dyld_NSAddImage) (const char *, unsigned long) = 0; 80 | static int (*dyld_NSIsSymbolNameDefinedInImage) (const struct mach_header *, const char *) = 0; 81 | static NSSymbol(*dyld_NSLookupSymbolInImage) 82 | (const struct mach_header *, const char *, unsigned long) = 0; 83 | 84 | /* Define this to make dlcompat reuse data block. This way in theory we save 85 | * a little bit of overhead. However we then couldn't correctly catch excess 86 | * calls to dlclose(). Hence we don't use this feature 87 | */ 88 | #undef REUSE_STATUS 89 | 90 | /* Size of the internal error message buffer (used by dlerror()) */ 91 | #define ERR_STR_LEN 251 92 | 93 | /* Maximum number of search paths supported by getSearchPath */ 94 | #define MAX_SEARCH_PATHS 32 95 | 96 | 97 | #define MAGIC_DYLIB_OFI ((NSObjectFileImage) 'DYOF') 98 | #define MAGIC_DYLIB_MOD ((NSModule) 'DYMO') 99 | 100 | /* internal flags */ 101 | #define DL_IN_LIST 0x01 102 | 103 | /* our mutex */ 104 | static pthread_mutex_t dlcompat_mutex; 105 | /* Our thread specific storage 106 | */ 107 | static pthread_key_t dlerror_key; 108 | 109 | struct dlthread 110 | { 111 | int lockcnt; 112 | unsigned char errset; 113 | char errstr[ERR_STR_LEN]; 114 | }; 115 | 116 | /* This is our central data structure. Whenever a module is loaded via 117 | * dlopen(), we create such a struct. 118 | */ 119 | struct dlstatus 120 | { 121 | struct dlstatus *next; /* pointer to next element in the linked list */ 122 | NSModule module; 123 | const struct mach_header *lib; 124 | int refs; /* reference count */ 125 | int mode; /* mode in which this module was loaded */ 126 | dev_t device; 127 | ino_t inode; 128 | int flags; /* Any internal flags we may need */ 129 | }; 130 | 131 | /* Head node of the dlstatus list */ 132 | static struct dlstatus mainStatus = { 0, MAGIC_DYLIB_MOD, NULL, -1, RTLD_GLOBAL, 0, 0, 0 }; 133 | static struct dlstatus *stqueue = &mainStatus; 134 | 135 | 136 | /* Storage for the last error message (used by dlerror()) */ 137 | /* static char err_str[ERR_STR_LEN]; */ 138 | /* static int err_filled = 0; */ 139 | 140 | /* Prototypes to internal functions */ 141 | static void debug(const char *fmt, ...); 142 | static void error(const char *str, ...); 143 | static const char *safegetenv(const char *s); 144 | static const char *searchList(void); 145 | static const char *getSearchPath(int i); 146 | static const char *getFullPath(int i, const char *file); 147 | static const struct stat *findFile(const char *file, const char **fullPath); 148 | static int isValidStatus(struct dlstatus *status); 149 | static inline int isFlagSet(int mode, int flag); 150 | static struct dlstatus *lookupStatus(const struct stat *sbuf); 151 | static void insertStatus(struct dlstatus *dls, const struct stat *sbuf); 152 | static int promoteLocalToGlobal(struct dlstatus *dls); 153 | static void *reference(struct dlstatus *dls, int mode); 154 | static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError); 155 | static struct dlstatus *allocStatus(void); 156 | static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode); 157 | static NSSymbol *search_linked_libs(const struct mach_header *mh, const char *symbol); 158 | static const char *get_lib_name(const struct mach_header *mh); 159 | static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod); 160 | static void dlcompat_init_func(void); 161 | static inline void dlcompat_init_check(void); 162 | static inline void dolock(void); 163 | static inline void dounlock(void); 164 | static void dlerrorfree(void *data); 165 | static void resetdlerror(void); 166 | static const struct mach_header *my_find_image(const char *name); 167 | static const struct mach_header *image_for_address(const void *address); 168 | static inline const char *dyld_error_str(void); 169 | 170 | #if FINK_BUILD 171 | /* Two Global Functions */ 172 | void *dlsym_prepend_underscore(void *handle, const char *symbol); 173 | void *dlsym_auto_underscore(void *handle, const char *symbol); 174 | 175 | /* And their _intern counterparts */ 176 | static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol); 177 | static void *dlsym_auto_underscore_intern(void *handle, const char *symbol); 178 | #endif 179 | 180 | /* Functions */ 181 | 182 | static void debug(const char *fmt, ...) 183 | { 184 | #if DEBUG > 1 185 | va_list arg; 186 | va_start(arg, fmt); 187 | fprintf(stderr, "DLDEBUG: "); 188 | vfprintf(stderr, fmt, arg); 189 | fprintf(stderr, "\n"); 190 | fflush(stderr); 191 | va_end(arg); 192 | #endif 193 | } 194 | 195 | static void error(const char *str, ...) 196 | { 197 | va_list arg; 198 | struct dlthread *tss; 199 | char * err_str; 200 | va_start(arg, str); 201 | tss = pthread_getspecific(dlerror_key); 202 | err_str = tss->errstr; 203 | strncpy(err_str, "dlcompat: ", ERR_STR_LEN); 204 | vsnprintf(err_str + 10, ERR_STR_LEN - 10, str, arg); 205 | va_end(arg); 206 | debug("ERROR: %s\n", err_str); 207 | tss->errset = 1; 208 | } 209 | 210 | static void warning(const char *str) 211 | { 212 | #if DEBUG > 0 213 | fprintf(stderr, "WARNING: dlcompat: %s\n", str); 214 | #endif 215 | } 216 | 217 | static const char *safegetenv(const char *s) 218 | { 219 | const char *ss = getenv(s); 220 | return ss ? ss : ""; 221 | } 222 | 223 | /* because this is only used for debugging and error reporting functions, we 224 | * don't really care about how elegant it is... it could use the load 225 | * commands to find the install name of the library, but... 226 | */ 227 | static const char *get_lib_name(const struct mach_header *mh) 228 | { 229 | unsigned long count = _dyld_image_count(); 230 | unsigned long i; 231 | const char *val = NULL; 232 | if (mh) 233 | { 234 | for (i = 0; i < count; i++) 235 | { 236 | if (mh == _dyld_get_image_header(i)) 237 | { 238 | val = _dyld_get_image_name(i); 239 | break; 240 | } 241 | } 242 | } 243 | return val; 244 | } 245 | 246 | /* Returns the mach_header for the module bu going through all the loaded images 247 | * and finding the one with the same name as the module. There really ought to be 248 | * an api for doing this, would be faster, but there isn't one right now 249 | */ 250 | static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod) 251 | { 252 | const char *mod_name = NSNameOfModule(mod); 253 | struct mach_header *mh = NULL; 254 | unsigned long count = _dyld_image_count(); 255 | unsigned long i; 256 | debug("Module name: %s", mod_name); 257 | for (i = 0; i < count; i++) 258 | { 259 | if (!strcmp(mod_name, _dyld_get_image_name(i))) 260 | { 261 | mh = _dyld_get_image_header(i); 262 | break; 263 | } 264 | } 265 | return mh; 266 | } 267 | 268 | 269 | /* Compute and return a list of all directories that we should search when 270 | * trying to locate a module. We first look at the values of LD_LIBRARY_PATH 271 | * and DYLD_LIBRARY_PATH, and then finally fall back to looking into 272 | * /usr/lib and /lib. Since both of the environments variables can contain a 273 | * list of colon seperated paths, we simply concat them and the two other paths 274 | * into one big string, which we then can easily parse. 275 | * Splitting this string into the actual path list is done by getSearchPath() 276 | */ 277 | static const char *searchList() 278 | { 279 | size_t buf_size; 280 | static char *buf=NULL; 281 | const char *ldlp = safegetenv("LD_LIBRARY_PATH"); 282 | const char *dyldlp = safegetenv("DYLD_LIBRARY_PATH"); 283 | const char *stdpath = getenv("DYLD_FALLBACK_LIBRARY_PATH"); 284 | if (!stdpath) 285 | stdpath = "/usr/local/lib:/lib:/usr/lib"; 286 | if (!buf) 287 | { 288 | buf_size = strlen(ldlp) + strlen(dyldlp) + strlen(stdpath) + 4; 289 | buf = malloc(buf_size); 290 | snprintf(buf, buf_size, "%s%s%s%s%s%c", dyldlp, (dyldlp[0] ? ":" : ""), ldlp, (ldlp[0] ? ":" : ""), 291 | stdpath, '\0'); 292 | } 293 | return buf; 294 | } 295 | 296 | /* Returns the ith search path from the list as computed by searchList() */ 297 | static const char *getSearchPath(int i) 298 | { 299 | static const char *list = 0; 300 | static char **path = (char **)0; 301 | static int end = 0; 302 | static int numsize = MAX_SEARCH_PATHS; 303 | static char **tmp; 304 | /* So we can call free() in the "destructor" we use i=-1 to return the alloc'd array */ 305 | if (i == -1) 306 | { 307 | return (const char*)path; 308 | } 309 | if (!path) 310 | { 311 | path = (char **)calloc(MAX_SEARCH_PATHS, sizeof(char **)); 312 | } 313 | if (!list && !end) 314 | list = searchList(); 315 | if (i >= (numsize)) 316 | { 317 | debug("Increasing size for long PATH"); 318 | tmp = (char **)calloc((MAX_SEARCH_PATHS + numsize), sizeof(char **)); 319 | if (tmp) 320 | { 321 | memcpy(tmp, path, sizeof(char **) * numsize); 322 | free(path); 323 | path = tmp; 324 | numsize += MAX_SEARCH_PATHS; 325 | } 326 | else 327 | { 328 | return 0; 329 | } 330 | } 331 | 332 | while (!path[i] && !end) 333 | { 334 | path[i] = strsep((char **)&list, ":"); 335 | 336 | if (path[i][0] == 0) 337 | path[i] = 0; 338 | end = (list == 0); 339 | } 340 | return path[i]; 341 | } 342 | 343 | static const char *getFullPath(int i, const char *file) 344 | { 345 | static char buf[PATH_MAX]; 346 | const char *path = getSearchPath(i); 347 | if (path) 348 | { 349 | snprintf(buf, PATH_MAX, "%s/%s", path, file); 350 | } 351 | return path ? buf : 0; 352 | } 353 | 354 | /* Given a file name, try to determine the full path for that file. Starts 355 | * its search in the current directory, and then tries all paths in the 356 | * search list in the order they are specified there. 357 | */ 358 | static const struct stat *findFile(const char *file, const char **fullPath) 359 | { 360 | int i = 0; 361 | static struct stat sbuf; 362 | char *fileName; 363 | debug("finding file %s", file); 364 | *fullPath = file; 365 | if (0 == stat(file, &sbuf)) 366 | return &sbuf; 367 | if (strchr(file, '/')) 368 | return 0; /* If the path had a / we don't look in env var places */ 369 | fileName = NULL; 370 | if (!fileName) 371 | fileName = (char *)file; 372 | while ((*fullPath = getFullPath(i++, fileName))) 373 | { 374 | if (0 == stat(*fullPath, &sbuf)) 375 | return &sbuf; 376 | } 377 | ; 378 | return 0; 379 | } 380 | 381 | /* Determine whether a given dlstatus is valid or not */ 382 | static int isValidStatus(struct dlstatus *status) 383 | { 384 | /* Walk the list to verify status is contained in it */ 385 | struct dlstatus *dls = stqueue; 386 | while (dls && status != dls) 387 | dls = dls->next; 388 | if (dls == 0) 389 | error("invalid handle"); 390 | else if ((dls->module == 0) || (dls->refs == 0)) 391 | error("handle to closed library"); 392 | else 393 | return TRUE; 394 | return FALSE; 395 | } 396 | 397 | static inline int isFlagSet(int mode, int flag) 398 | { 399 | return (mode & flag) == flag; 400 | } 401 | 402 | static struct dlstatus *lookupStatus(const struct stat *sbuf) 403 | { 404 | struct dlstatus *dls = stqueue; 405 | debug("looking for status"); 406 | while (dls && ( /* isFlagSet(dls->mode, RTLD_UNSHARED) */ 0 407 | || sbuf->st_dev != dls->device || sbuf->st_ino != dls->inode)) 408 | dls = dls->next; 409 | return dls; 410 | } 411 | 412 | static void insertStatus(struct dlstatus *dls, const struct stat *sbuf) 413 | { 414 | debug("inserting status"); 415 | dls->inode = sbuf->st_ino; 416 | dls->device = sbuf->st_dev; 417 | dls->refs = 0; 418 | dls->mode = 0; 419 | if ((dls->flags & DL_IN_LIST) == 0) 420 | { 421 | dls->next = stqueue; 422 | stqueue = dls; 423 | dls->flags |= DL_IN_LIST; 424 | } 425 | } 426 | 427 | static struct dlstatus *allocStatus() 428 | { 429 | struct dlstatus *dls; 430 | #ifdef REUSE_STATUS 431 | dls = stqueue; 432 | while (dls && dls->module) 433 | dls = dls->next; 434 | if (!dls) 435 | #endif 436 | dls = calloc(sizeof(*dls),1); 437 | return dls; 438 | } 439 | 440 | static int promoteLocalToGlobal(struct dlstatus *dls) 441 | { 442 | static int (*p) (NSModule module) = 0; 443 | debug("promoting"); 444 | if (!p) 445 | _dyld_func_lookup("__dyld_NSMakePrivateModulePublic", (unsigned long *)&p); 446 | return (dls->module == MAGIC_DYLIB_MOD) || (p && p(dls->module)); 447 | } 448 | 449 | static void *reference(struct dlstatus *dls, int mode) 450 | { 451 | if (dls) 452 | { 453 | if (dls->module == MAGIC_DYLIB_MOD && isFlagSet(mode, RTLD_LOCAL)) 454 | { 455 | warning("trying to open a .dylib with RTLD_LOCAL"); 456 | error("unable to open a .dylib with RTLD_LOCAL"); 457 | return NULL; 458 | } 459 | if (isFlagSet(mode, RTLD_GLOBAL) && 460 | !isFlagSet(dls->mode, RTLD_GLOBAL) && !promoteLocalToGlobal(dls)) 461 | { 462 | error("unable to promote local module to global"); 463 | return NULL; 464 | } 465 | dls->mode |= mode; 466 | dls->refs++; 467 | } 468 | else 469 | debug("reference called with NULL argument"); 470 | 471 | return dls; 472 | } 473 | 474 | static const struct mach_header *my_find_image(const char *name) 475 | { 476 | const struct mach_header *mh = 0; 477 | const char *id = NULL; 478 | int i = _dyld_image_count(); 479 | int j; 480 | mh = (struct mach_header *) 481 | dyld_NSAddImage(name, NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED | 482 | NSADDIMAGE_OPTION_RETURN_ON_ERROR); 483 | if (!mh) 484 | { 485 | for (j = 0; j < i; j++) 486 | { 487 | id = _dyld_get_image_name(j); 488 | if (!strcmp(id, name)) 489 | { 490 | mh = _dyld_get_image_header(j); 491 | break; 492 | } 493 | } 494 | } 495 | return mh; 496 | } 497 | 498 | /* 499 | * dyld adds libraries by first adding the directly dependant libraries in link order, and 500 | * then adding the dependencies for those libraries, so we should do the same... but we don't 501 | * bother adding the extra dependencies, if the symbols are neither in the loaded image nor 502 | * any of it's direct dependencies, then it probably isn't there. 503 | */ 504 | NSSymbol *search_linked_libs(const struct mach_header * mh, const char *symbol) 505 | { 506 | unsigned int n; 507 | struct load_command *lc = 0; 508 | struct mach_header *wh; 509 | NSSymbol *nssym = 0; 510 | if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) 511 | { 512 | lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); 513 | for (n = 0; n < mh->ncmds; n++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) 514 | { 515 | if ((LC_LOAD_DYLIB == lc->cmd) || (LC_LOAD_WEAK_DYLIB == lc->cmd)) 516 | { 517 | if ((wh = (struct mach_header *) 518 | my_find_image((char *)(((struct dylib_command *)lc)->dylib.name.offset + 519 | (char *)lc)))) 520 | { 521 | if (dyld_NSIsSymbolNameDefinedInImage(wh, symbol)) 522 | { 523 | nssym = dyld_NSLookupSymbolInImage(wh, 524 | symbol, 525 | NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | 526 | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); 527 | break; 528 | } 529 | } 530 | } 531 | } 532 | if ((!nssym) && NSIsSymbolNameDefined(symbol)) 533 | { 534 | /* I've never seen this debug message...*/ 535 | debug("Symbol \"%s\" is defined but was not found", symbol); 536 | } 537 | } 538 | return nssym; 539 | } 540 | 541 | /* Up to the caller to free() returned string */ 542 | static inline const char *dyld_error_str() 543 | { 544 | NSLinkEditErrors dylder; 545 | int dylderno; 546 | const char *dylderrstr; 547 | const char *dyldfile; 548 | const char* retStr = NULL; 549 | NSLinkEditError(&dylder, &dylderno, &dyldfile, &dylderrstr); 550 | if (dylderrstr && strlen(dylderrstr)) 551 | { 552 | retStr = malloc(strlen(dylderrstr) +1); 553 | strcpy((char*)retStr,dylderrstr); 554 | } 555 | return retStr; 556 | } 557 | 558 | static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError) 559 | { 560 | NSSymbol *nssym = 0; 561 | #ifdef __GCC__ 562 | void *caller = __builtin_return_address(1); /* Be *very* careful about inlining */ 563 | #else 564 | void *caller = NULL; 565 | #endif 566 | const struct mach_header *caller_mh = 0; 567 | const char* savedErrorStr = NULL; 568 | resetdlerror(); 569 | #ifndef RTLD_SELF 570 | #define RTLD_SELF ((void *) -3) 571 | #endif 572 | if (NULL == dls) 573 | dls = RTLD_SELF; 574 | if ((RTLD_NEXT == dls) || (RTLD_SELF == dls)) 575 | { 576 | if (dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage && caller) 577 | { 578 | caller_mh = image_for_address(caller); 579 | if (RTLD_SELF == dls) 580 | { 581 | /* FIXME: We should be using the NSModule api, if SELF is an MH_BUNDLE 582 | * But it appears to work anyway, and looking at the code in dyld_libfuncs.c 583 | * this is acceptable. 584 | */ 585 | if (dyld_NSIsSymbolNameDefinedInImage(caller_mh, symbol)) 586 | { 587 | nssym = dyld_NSLookupSymbolInImage(caller_mh, 588 | symbol, 589 | NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | 590 | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); 591 | } 592 | } 593 | if (!nssym) 594 | { 595 | if (RTLD_SELF == dls) 596 | savedErrorStr = dyld_error_str(); 597 | nssym = search_linked_libs(caller_mh, symbol); 598 | } 599 | } 600 | else 601 | { 602 | if (canSetError) 603 | error("RTLD_SELF and RTLD_NEXT are not supported"); 604 | return NULL; 605 | } 606 | } 607 | if (!nssym) 608 | { 609 | 610 | if (RTLD_DEFAULT == dls) 611 | { 612 | dls = &mainStatus; 613 | } 614 | if (!isValidStatus(dls)) 615 | return NULL; 616 | 617 | if (dls->module != MAGIC_DYLIB_MOD) 618 | { 619 | nssym = NSLookupSymbolInModule(dls->module, symbol); 620 | if (!nssym && NSIsSymbolNameDefined(symbol)) 621 | { 622 | debug("Searching dependencies"); 623 | savedErrorStr = dyld_error_str(); 624 | nssym = search_linked_libs(get_mach_header_from_NSModule(dls->module), symbol); 625 | } 626 | } 627 | else if (dls->lib && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) 628 | { 629 | if (dyld_NSIsSymbolNameDefinedInImage(dls->lib, symbol)) 630 | { 631 | nssym = dyld_NSLookupSymbolInImage(dls->lib, 632 | symbol, 633 | NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | 634 | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); 635 | } 636 | else if (NSIsSymbolNameDefined(symbol)) 637 | { 638 | debug("Searching dependencies"); 639 | savedErrorStr = dyld_error_str(); 640 | nssym = search_linked_libs(dls->lib, symbol); 641 | } 642 | } 643 | else if (dls->module == MAGIC_DYLIB_MOD) 644 | { 645 | /* Global context, use NSLookupAndBindSymbol */ 646 | if (NSIsSymbolNameDefined(symbol)) 647 | { 648 | /* There doesn't seem to be a return on error option for this call??? 649 | this is potentially broken, if binding fails, it will improperly 650 | exit the application. */ 651 | nssym = NSLookupAndBindSymbol(symbol); 652 | } 653 | else 654 | { 655 | if (savedErrorStr) 656 | free((char*)savedErrorStr); 657 | savedErrorStr = malloc(256); 658 | snprintf((char*)savedErrorStr, 256, "Symbol \"%s\" not in global context",symbol); 659 | } 660 | } 661 | } 662 | /* Error reporting */ 663 | if (!nssym) 664 | { 665 | if (!savedErrorStr || !strlen(savedErrorStr)) 666 | { 667 | if (savedErrorStr) 668 | free((char*)savedErrorStr); 669 | savedErrorStr = malloc(256); 670 | snprintf((char*)savedErrorStr, 256,"Symbol \"%s\" not found",symbol); 671 | } 672 | if (canSetError) 673 | { 674 | error(savedErrorStr); 675 | } 676 | else 677 | { 678 | debug(savedErrorStr); 679 | } 680 | if (savedErrorStr) 681 | free((char*)savedErrorStr); 682 | return NULL; 683 | } 684 | return NSAddressOfSymbol(nssym); 685 | } 686 | 687 | static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode) 688 | { 689 | NSObjectFileImage ofi = 0; 690 | NSObjectFileImageReturnCode ofirc; 691 | struct dlstatus *dls; 692 | NSLinkEditErrors ler; 693 | int lerno; 694 | const char *errstr; 695 | const char *file; 696 | void (*init) (void); 697 | ofirc = NSCreateObjectFileImageFromFile(path, &ofi); 698 | switch (ofirc) 699 | { 700 | case NSObjectFileImageSuccess: 701 | break; 702 | case NSObjectFileImageInappropriateFile: 703 | if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) 704 | { 705 | if (isFlagSet(mode, RTLD_LOCAL)) 706 | { 707 | warning("trying to open a .dylib with RTLD_LOCAL"); 708 | error("unable to open this file with RTLD_LOCAL"); 709 | return NULL; 710 | } 711 | } 712 | else 713 | { 714 | error("opening this file is unsupported on this system"); 715 | return NULL; 716 | } 717 | break; 718 | case NSObjectFileImageFailure: 719 | error("object file setup failure"); 720 | return NULL; 721 | case NSObjectFileImageArch: 722 | error("no object for this architecture"); 723 | return NULL; 724 | case NSObjectFileImageFormat: 725 | error("bad object file format"); 726 | return NULL; 727 | case NSObjectFileImageAccess: 728 | error("can't read object file"); 729 | return NULL; 730 | default: 731 | error("unknown error from NSCreateObjectFileImageFromFile()"); 732 | return NULL; 733 | } 734 | dls = lookupStatus(sbuf); 735 | if (!dls) 736 | { 737 | dls = allocStatus(); 738 | } 739 | if (!dls) 740 | { 741 | error("unable to allocate memory"); 742 | return NULL; 743 | } 744 | // dls->lib = 0; 745 | if (ofirc == NSObjectFileImageInappropriateFile) 746 | { 747 | if ((dls->lib = dyld_NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR))) 748 | { 749 | debug("Dynamic lib loaded at %ld", dls->lib); 750 | ofi = MAGIC_DYLIB_OFI; 751 | dls->module = MAGIC_DYLIB_MOD; 752 | ofirc = NSObjectFileImageSuccess; 753 | /* Although it is possible with a bit of work to modify this so it works and 754 | functions with RTLD_NOW, I don't deem it necessary at the moment */ 755 | } 756 | if (!(dls->module)) 757 | { 758 | NSLinkEditError(&ler, &lerno, &file, &errstr); 759 | if (!errstr || (!strlen(errstr))) 760 | error("Can't open this file type"); 761 | else 762 | error(errstr); 763 | if ((dls->flags & DL_IN_LIST) == 0) 764 | { 765 | free(dls); 766 | } 767 | return NULL; 768 | } 769 | } 770 | else 771 | { 772 | dls->module = NSLinkModule(ofi, path, 773 | NSLINKMODULE_OPTION_RETURN_ON_ERROR | 774 | NSLINKMODULE_OPTION_PRIVATE | 775 | (isFlagSet(mode, RTLD_NOW) ? NSLINKMODULE_OPTION_BINDNOW : 0)); 776 | NSDestroyObjectFileImage(ofi); 777 | if (dls->module) 778 | { 779 | dls->lib = get_mach_header_from_NSModule(dls->module); 780 | } 781 | } 782 | if (!dls->module) 783 | { 784 | NSLinkEditError(&ler, &lerno, &file, &errstr); 785 | if ((dls->flags & DL_IN_LIST) == 0) 786 | { 787 | free(dls); 788 | } 789 | error(errstr); 790 | return NULL; 791 | } 792 | 793 | insertStatus(dls, sbuf); 794 | dls = reference(dls, mode); 795 | if ((init = dlsymIntern(dls, "__init", 0))) 796 | { 797 | debug("calling _init()"); 798 | init(); 799 | } 800 | return dls; 801 | } 802 | 803 | inline static void dlcompat_init_check(void) 804 | { 805 | static pthread_mutex_t l = PTHREAD_MUTEX_INITIALIZER; 806 | static int init_done = 0; 807 | 808 | pthread_mutex_lock(&l); 809 | if (!init_done) { 810 | dlcompat_init_func(); 811 | init_done = 1; 812 | } 813 | pthread_mutex_unlock(&l); 814 | } 815 | 816 | static void dlcompat_init_func(void) 817 | { 818 | _dyld_func_lookup("__dyld_NSAddImage", (unsigned long *)&dyld_NSAddImage); 819 | _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage", 820 | (unsigned long *)&dyld_NSIsSymbolNameDefinedInImage); 821 | _dyld_func_lookup("__dyld_NSLookupSymbolInImage", (unsigned long *)&dyld_NSLookupSymbolInImage); 822 | if (pthread_mutex_init(&dlcompat_mutex, NULL)) 823 | exit(1); 824 | if (pthread_key_create(&dlerror_key, &dlerrorfree)) 825 | exit(1); 826 | } 827 | 828 | static void resetdlerror() 829 | { 830 | struct dlthread *tss; 831 | tss = pthread_getspecific(dlerror_key); 832 | tss->errset = 0; 833 | } 834 | 835 | static void dlerrorfree(void *data) 836 | { 837 | free(data); 838 | } 839 | 840 | /* We kind of want a recursive lock here, but meet a little trouble 841 | * because they are not available pre OS X 10.2, so we fake it 842 | * using thread specific storage to keep a lock count 843 | */ 844 | static inline void dolock(void) 845 | { 846 | int err = 0; 847 | struct dlthread *tss; 848 | dlcompat_init_check(); 849 | tss = pthread_getspecific(dlerror_key); 850 | if (!tss) 851 | { 852 | tss = malloc(sizeof(struct dlthread)); 853 | tss->lockcnt = 0; 854 | tss->errset = 0; 855 | if (pthread_setspecific(dlerror_key, tss)) 856 | { 857 | fprintf(stderr,"dlcompat: pthread_setspecific failed\n"); 858 | exit(1); 859 | } 860 | } 861 | if (!tss->lockcnt) 862 | err = pthread_mutex_lock(&dlcompat_mutex); 863 | tss->lockcnt = tss->lockcnt +1; 864 | if (err) 865 | exit(err); 866 | } 867 | 868 | static inline void dounlock(void) 869 | { 870 | int err = 0; 871 | struct dlthread *tss; 872 | tss = pthread_getspecific(dlerror_key); 873 | tss->lockcnt = tss->lockcnt -1; 874 | if (!tss->lockcnt) 875 | err = pthread_mutex_unlock(&dlcompat_mutex); 876 | if (err) 877 | exit(err); 878 | } 879 | 880 | void *dlopen(const char *path, int mode) 881 | { 882 | const struct stat *sbuf; 883 | struct dlstatus *dls; 884 | const char *fullPath; 885 | 886 | dolock(); 887 | resetdlerror(); 888 | if (!path) 889 | { 890 | dls = &mainStatus; 891 | goto dlopenok; 892 | } 893 | if (!(sbuf = findFile(path, &fullPath))) 894 | { 895 | error("file \"%s\" not found", path); 896 | goto dlopenerror; 897 | } 898 | /* Now checks that it hasn't been closed already */ 899 | if ((dls = lookupStatus(sbuf)) && (dls->refs > 0)) 900 | { 901 | debug("status found"); 902 | dls = reference(dls, mode); 903 | goto dlopenok; 904 | } 905 | #ifdef RTLD_NOLOAD 906 | if (isFlagSet(mode, RTLD_NOLOAD)) 907 | { 908 | error("no existing handle and RTLD_NOLOAD specified"); 909 | goto dlopenerror; 910 | } 911 | #endif 912 | if (isFlagSet(mode, RTLD_LAZY) && isFlagSet(mode, RTLD_NOW)) 913 | { 914 | error("how can I load something both RTLD_LAZY and RTLD_NOW?"); 915 | goto dlopenerror; 916 | } 917 | dls = loadModule(fullPath, sbuf, mode); 918 | 919 | dlopenok: 920 | dounlock(); 921 | return (void *)dls; 922 | dlopenerror: 923 | dounlock(); 924 | return NULL; 925 | } 926 | 927 | #if !FINK_BUILD 928 | void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol) 929 | { 930 | int sym_len = strlen(symbol); 931 | void *value = NULL; 932 | char *malloc_sym = NULL; 933 | dolock(); 934 | malloc_sym = malloc(sym_len + 2); 935 | if (malloc_sym) 936 | { 937 | sprintf(malloc_sym, "_%s", symbol); 938 | value = dlsymIntern(handle, malloc_sym, 1); 939 | free(malloc_sym); 940 | } 941 | else 942 | { 943 | error("Unable to allocate memory"); 944 | goto dlsymerror; 945 | } 946 | dounlock(); 947 | return value; 948 | dlsymerror: 949 | dounlock(); 950 | return NULL; 951 | } 952 | #endif 953 | 954 | #if FINK_BUILD 955 | 956 | void *dlsym_prepend_underscore(void *handle, const char *symbol) 957 | { 958 | void *answer; 959 | dolock(); 960 | answer = dlsym_prepend_underscore_intern(handle, symbol); 961 | dounlock(); 962 | return answer; 963 | } 964 | 965 | static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol) 966 | { 967 | /* 968 | * A quick and easy way for porting packages which call dlsym(handle,"sym") 969 | * If the porter adds -Ddlsym=dlsym_prepend_underscore to the CFLAGS then 970 | * this function will be called, and will add the required underscore. 971 | * 972 | * Note that I haven't figured out yet which should be "standard", prepend 973 | * the underscore always, or not at all. These global functions need to go away 974 | * for opendarwin. 975 | */ 976 | int sym_len = strlen(symbol); 977 | void *value = NULL; 978 | char *malloc_sym = NULL; 979 | malloc_sym = malloc(sym_len + 2); 980 | if (malloc_sym) 981 | { 982 | sprintf(malloc_sym, "_%s", symbol); 983 | value = dlsymIntern(handle, malloc_sym, 1); 984 | free(malloc_sym); 985 | } 986 | else 987 | { 988 | error("Unable to allocate memory"); 989 | } 990 | return value; 991 | } 992 | 993 | void *dlsym_auto_underscore(void *handle, const char *symbol) 994 | { 995 | void *answer; 996 | dolock(); 997 | answer = dlsym_auto_underscore_intern(handle, symbol); 998 | dounlock(); 999 | return answer; 1000 | 1001 | } 1002 | static void *dlsym_auto_underscore_intern(void *handle, const char *symbol) 1003 | { 1004 | struct dlstatus *dls = handle; 1005 | void *addr = 0; 1006 | addr = dlsymIntern(dls, symbol, 0); 1007 | if (!addr) 1008 | addr = dlsym_prepend_underscore_intern(handle, symbol); 1009 | return addr; 1010 | } 1011 | 1012 | 1013 | void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol) 1014 | { 1015 | struct dlstatus *dls = handle; 1016 | void *addr = 0; 1017 | dolock(); 1018 | addr = dlsymIntern(dls, symbol, 1); 1019 | dounlock(); 1020 | return addr; 1021 | } 1022 | #endif 1023 | 1024 | int dlclose(void *handle) 1025 | { 1026 | struct dlstatus *dls = handle; 1027 | dolock(); 1028 | resetdlerror(); 1029 | if (!isValidStatus(dls)) 1030 | { 1031 | goto dlcloseerror; 1032 | } 1033 | if (dls->module == MAGIC_DYLIB_MOD) 1034 | { 1035 | const char *name; 1036 | if (!dls->lib) 1037 | { 1038 | name = "global context"; 1039 | } 1040 | else 1041 | { 1042 | name = get_lib_name(dls->lib); 1043 | } 1044 | warning("trying to close a .dylib!"); 1045 | error("Not closing \"%s\" - dynamic libraries cannot be closed", name); 1046 | goto dlcloseerror; 1047 | } 1048 | if (!dls->module) 1049 | { 1050 | error("module already closed"); 1051 | goto dlcloseerror; 1052 | } 1053 | 1054 | if (dls->refs == 1) 1055 | { 1056 | unsigned long options = 0; 1057 | void (*fini) (void); 1058 | if ((fini = dlsymIntern(dls, "__fini", 0))) 1059 | { 1060 | debug("calling _fini()"); 1061 | fini(); 1062 | } 1063 | options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES; 1064 | #ifdef RTLD_NODELETE 1065 | if (isFlagSet(dls->mode, RTLD_NODELETE)) 1066 | options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED; 1067 | #endif 1068 | if (!NSUnLinkModule(dls->module, options)) 1069 | { 1070 | error("unable to unlink module"); 1071 | goto dlcloseerror; 1072 | } 1073 | dls->refs--; 1074 | dls->module = 0; 1075 | /* Note: the dlstatus struct dls is neither removed from the list 1076 | * nor is the memory it occupies freed. This shouldn't pose a 1077 | * problem in mostly all cases, though. 1078 | */ 1079 | } 1080 | dounlock(); 1081 | return 0; 1082 | dlcloseerror: 1083 | dounlock(); 1084 | return 1; 1085 | } 1086 | 1087 | char *dlerror(void) 1088 | { 1089 | struct dlthread *tss; 1090 | const char * err_str = NULL; 1091 | dlcompat_init_check(); 1092 | tss = pthread_getspecific(dlerror_key); 1093 | if (tss != NULL && tss->errset != 0) { 1094 | tss->errset = 0; 1095 | err_str = tss->errstr; 1096 | } 1097 | return (err_str); 1098 | } 1099 | 1100 | /* Given an address, return the mach_header for the image containing it 1101 | * or zero if the given address is not contained in any loaded images. 1102 | */ 1103 | const struct mach_header *image_for_address(const void *address) 1104 | { 1105 | unsigned long i; 1106 | unsigned long j; 1107 | unsigned long count = _dyld_image_count(); 1108 | struct mach_header *mh = 0; 1109 | struct load_command *lc = 0; 1110 | unsigned long addr = NULL; 1111 | for (i = 0; i < count; i++) 1112 | { 1113 | addr = (unsigned long)address - _dyld_get_image_vmaddr_slide(i); 1114 | mh = _dyld_get_image_header(i); 1115 | if (mh) 1116 | { 1117 | lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); 1118 | for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) 1119 | { 1120 | if (LC_SEGMENT == lc->cmd && 1121 | addr >= ((struct segment_command *)lc)->vmaddr && 1122 | addr < 1123 | ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize) 1124 | { 1125 | goto image_found; 1126 | } 1127 | } 1128 | } 1129 | mh = 0; 1130 | } 1131 | image_found: 1132 | return mh; 1133 | } 1134 | 1135 | int dladdr(const void * dl_restrict p, Dl_info * dl_restrict info) 1136 | { 1137 | /* 1138 | FIXME: USe the routine image_for_address. 1139 | */ 1140 | unsigned long i; 1141 | unsigned long j; 1142 | unsigned long count = _dyld_image_count(); 1143 | struct mach_header *mh = 0; 1144 | struct load_command *lc = 0; 1145 | unsigned long addr = NULL; 1146 | unsigned long table_off = (unsigned long)0; 1147 | int found = 0; 1148 | if (!info) 1149 | return 0; 1150 | dolock(); 1151 | resetdlerror(); 1152 | info->dli_fname = 0; 1153 | info->dli_fbase = 0; 1154 | info->dli_sname = 0; 1155 | info->dli_saddr = 0; 1156 | /* Some of this was swiped from code posted by Douglas Davidson 1157 | * to darwin-development AT lists DOT apple DOT com and slightly modified 1158 | */ 1159 | for (i = 0; i < count; i++) 1160 | { 1161 | addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(i); 1162 | mh = _dyld_get_image_header(i); 1163 | if (mh) 1164 | { 1165 | lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); 1166 | for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) 1167 | { 1168 | if (LC_SEGMENT == lc->cmd && 1169 | addr >= ((struct segment_command *)lc)->vmaddr && 1170 | addr < 1171 | ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize) 1172 | { 1173 | info->dli_fname = _dyld_get_image_name(i); 1174 | info->dli_fbase = (void *)mh; 1175 | found = 1; 1176 | break; 1177 | } 1178 | } 1179 | if (found) 1180 | break; 1181 | } 1182 | } 1183 | if (!found) 1184 | { 1185 | dounlock(); 1186 | return 0; 1187 | } 1188 | lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); 1189 | for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) 1190 | { 1191 | if (LC_SEGMENT == lc->cmd) 1192 | { 1193 | if (!strcmp(((struct segment_command *)lc)->segname, "__LINKEDIT")) 1194 | break; 1195 | } 1196 | } 1197 | table_off = 1198 | ((unsigned long)((struct segment_command *)lc)->vmaddr) - 1199 | ((unsigned long)((struct segment_command *)lc)->fileoff) + _dyld_get_image_vmaddr_slide(i); 1200 | debug("table off %x", table_off); 1201 | 1202 | lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); 1203 | for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) 1204 | { 1205 | if (LC_SYMTAB == lc->cmd) 1206 | { 1207 | 1208 | struct nlist *symtable = (struct nlist *)(((struct symtab_command *)lc)->symoff + table_off); 1209 | unsigned long numsyms = ((struct symtab_command *)lc)->nsyms; 1210 | struct nlist *nearest = NULL; 1211 | unsigned long diff = 0xffffffff; 1212 | unsigned long strtable = (unsigned long)(((struct symtab_command *)lc)->stroff + table_off); 1213 | debug("symtable %x", symtable); 1214 | for (i = 0; i < numsyms; i++) 1215 | { 1216 | /* Ignore the following kinds of Symbols */ 1217 | if ((!symtable->n_value) /* Undefined */ 1218 | || (symtable->n_type >= N_PEXT) /* Debug symbol */ 1219 | || (!(symtable->n_type & N_EXT)) /* Local Symbol */ 1220 | ) 1221 | { 1222 | symtable++; 1223 | continue; 1224 | } 1225 | if ((addr >= symtable->n_value) && (diff >= (symtable->n_value - addr))) 1226 | { 1227 | diff = (unsigned long)symtable->n_value - addr; 1228 | nearest = symtable; 1229 | } 1230 | symtable++; 1231 | } 1232 | if (nearest) 1233 | { 1234 | info->dli_saddr = nearest->n_value + ((void *)p - addr); 1235 | info->dli_sname = (char *)(strtable + nearest->n_un.n_strx); 1236 | } 1237 | } 1238 | } 1239 | dounlock(); 1240 | return 1; 1241 | } 1242 | 1243 | 1244 | /* 1245 | * Implement the dlfunc() interface, which behaves exactly the same as 1246 | * dlsym() except that it returns a function pointer instead of a data 1247 | * pointer. This can be used by applications to avoid compiler warnings 1248 | * about undefined behavior, and is intended as prior art for future 1249 | * POSIX standardization. This function requires that all pointer types 1250 | * have the same representation, which is true on all platforms FreeBSD 1251 | * runs on, but is not guaranteed by the C standard. 1252 | */ 1253 | #if 0 1254 | dlfunc_t dlfunc(void * dl_restrict handle, const char * dl_restrict symbol) 1255 | { 1256 | union 1257 | { 1258 | void *d; 1259 | dlfunc_t f; 1260 | } rv; 1261 | int sym_len = strlen(symbol); 1262 | char *malloc_sym = NULL; 1263 | dolock(); 1264 | malloc_sym = malloc(sym_len + 2); 1265 | if (malloc_sym) 1266 | { 1267 | sprintf(malloc_sym, "_%s", symbol); 1268 | rv.d = dlsymIntern(handle, malloc_sym, 1); 1269 | free(malloc_sym); 1270 | } 1271 | else 1272 | { 1273 | error("Unable to allocate memory"); 1274 | goto dlfuncerror; 1275 | } 1276 | dounlock(); 1277 | return rv.f; 1278 | dlfuncerror: 1279 | dounlock(); 1280 | return NULL; 1281 | } 1282 | #endif 1283 | -------------------------------------------------------------------------------- /src/http.c: -------------------------------------------------------------------------------- 1 | /* Asterisk Manager Proxy 2 | Copyright (c) 2005-2008 David C. Troy 3 | 4 | This program is free software, distributed under the terms of 5 | the GNU General Public License. 6 | 7 | http.c 8 | HTTP Input Handler 9 | */ 10 | 11 | #include "astmanproxy.h" 12 | 13 | // SwapChar: This routine swaps one character for another 14 | void SwapChar(char *pOriginal, char cBad, char cGood) { 15 | int i; // generic counter variable 16 | 17 | // Loop through the input string (cOriginal), character by 18 | // character, replacing each instance of cBad with cGood 19 | 20 | i = 0; 21 | while (pOriginal[i]) { 22 | if (pOriginal[i] == cBad) pOriginal[i] = cGood; 23 | i++; 24 | } 25 | } 26 | 27 | // IntFromHex: A subroutine to unescape escaped characters. 28 | static int IntFromHex(char *pChars) { 29 | int Hi; // holds high byte 30 | int Lo; // holds low byte 31 | int Result; // holds result 32 | 33 | // Get the value of the first byte to Hi 34 | 35 | Hi = pChars[0]; 36 | if ('0' <= Hi && Hi <= '9') { 37 | Hi -= '0'; 38 | } else 39 | if ('a' <= Hi && Hi <= 'f') { 40 | Hi -= ('a'-10); 41 | } else 42 | if ('A' <= Hi && Hi <= 'F') { 43 | Hi -= ('A'-10); 44 | } 45 | 46 | // Get the value of the second byte to Lo 47 | 48 | Lo = pChars[1]; 49 | if ('0' <= Lo && Lo <= '9') { 50 | Lo -= '0'; 51 | } else 52 | if ('a' <= Lo && Lo <= 'f') { 53 | Lo -= ('a'-10); 54 | } else 55 | if ('A' <= Lo && Lo <= 'F') { 56 | Lo -= ('A'-10); 57 | } 58 | Result = Lo + (16 * Hi); 59 | return (Result); 60 | } 61 | 62 | // URLDecode: This routine loops through the string pEncoded 63 | // (passed as a parameter), and decodes it in place. It checks for 64 | // escaped values, and changes all plus signs to spaces. The result 65 | // is a normalized string. It calls the two subroutines directly 66 | // above in this listing, IntFromHex() and SwapChar(). 67 | 68 | void URLDecode(char *pEncoded) { 69 | char *pDecoded; // generic pointer 70 | 71 | // First, change those pesky plusses to spaces 72 | SwapChar (pEncoded, '+', ' '); 73 | 74 | // Now, loop through looking for escapes 75 | pDecoded = pEncoded; 76 | while (*pEncoded) { 77 | if (*pEncoded=='%') { 78 | // A percent sign followed by two hex digits means 79 | // that the digits represent an escaped character. We 80 | // must decode it. 81 | 82 | pEncoded++; 83 | if (isxdigit(pEncoded[0]) && isxdigit(pEncoded[1])) { 84 | *pDecoded++ = (char) IntFromHex(pEncoded); 85 | pEncoded += 2; 86 | } 87 | } else { 88 | *pDecoded ++ = *pEncoded++; 89 | } 90 | } 91 | *pDecoded = '\0'; 92 | } 93 | 94 | int ParseHTTPInput(char *buf, struct message *m) { 95 | char *n, *v; 96 | 97 | n = buf; 98 | while ( (v = strstr(n, "=")) ) { 99 | v += 1; 100 | debugmsg("n: %s, v: %s", n, v); 101 | strncat(m->headers[m->hdrcount], n, v-n-1); 102 | strcat(m->headers[m->hdrcount], ": "); 103 | 104 | if ( (n = strstr(v, "&")) ) { 105 | n += 1; 106 | } else { 107 | n = (v + strlen(v) + 1); 108 | } 109 | strncat(m->headers[m->hdrcount], v, n-v-1); 110 | debugmsg("got hdr: %s", m->headers[m->hdrcount]); 111 | m->hdrcount++; 112 | } 113 | 114 | return (m->hdrcount > 0); 115 | } 116 | 117 | int HTTPHeader(struct mansession *s, char *status) { 118 | 119 | 120 | time_t t; 121 | struct tm tm; 122 | char date[80]; 123 | char ctype[15], hdr[MAX_LEN]; 124 | 125 | time(&t); 126 | localtime_r(&t, &tm); 127 | strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm); 128 | 129 | if ( !strcasecmp("xml", s->output->formatname) ) 130 | sprintf(ctype, "text/xml"); 131 | else 132 | sprintf(ctype, "text/plain"); 133 | 134 | if (!strcmp("200 OK", status) ) 135 | sprintf(hdr, 136 | "HTTP/1.1 %s\r\n" 137 | "Date: %s\r\n" 138 | "Content-Type: %s\r\n" 139 | "Connection: close\r\n" 140 | "Server: %s/%s\r\n\r\n", status, 141 | date, ctype, PROXY_BANNER, PROXY_VERSION); 142 | else 143 | sprintf(hdr, 144 | "HTTP/1.1 %s\r\n" 145 | "Date: %s\r\n" 146 | "Status: %s\r\n" 147 | "Server: %s/%s\r\n\r\n", status, date, status, PROXY_BANNER, PROXY_VERSION); 148 | 149 | pthread_mutex_lock(&s->lock); 150 | s->inputcomplete = 1; 151 | ast_carefulwrite(s->fd, hdr, strlen(hdr), s->writetimeout); 152 | pthread_mutex_unlock(&s->lock); 153 | debugmsg("http header: %s", hdr); 154 | 155 | return 0; 156 | } 157 | 158 | int _read(struct mansession *s, struct message *m) { 159 | 160 | /* Note: No single line may be longer than MAX_LEN/s->inbuf, as per get_input */ 161 | /* No HTTP Input may be longer than BUFSIZE */ 162 | 163 | char line[MAX_LEN], method[10], formdata[MAX_LEN], status[15]; 164 | char *tmp; 165 | int res, clength = 0; 166 | 167 | memset(method, 0, sizeof method); 168 | memset(formdata, 0, sizeof formdata); 169 | memset(status, 0, sizeof status); 170 | 171 | /* for http, don't do get_input forever */ 172 | for (;;) { 173 | 174 | if (s->inputcomplete && !s->outputcomplete) { 175 | sleep(1); 176 | continue; 177 | } else if (s->inputcomplete && s->outputcomplete) 178 | return -1; 179 | 180 | memset(line, 0, sizeof line); 181 | res = get_input(s, line); 182 | debugmsg("res=%d, line: %s",res, line); 183 | 184 | if (res > 0) { 185 | debugmsg("Got http: %s", line); 186 | 187 | if ( !clength && !strncasecmp(line, "Content-Length: ", 16) ) 188 | clength = atoi(line+16); 189 | 190 | if (!*method) { 191 | if ( !strncmp(line,"POST",4) ) { 192 | strncpy(method, line, 4); 193 | } else if ( !strncmp(line,"GET",3)) { 194 | if ( strlen(line) > 14 && (tmp = strcasestr(line, " HTTP")) ) { 195 | /* GET / HTTP/1.1 ---- this is bad */ 196 | /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */ 197 | strncpy(method, line, 3); 198 | memcpy(formdata, line+6, tmp-line-6); 199 | sprintf(status, "200 OK"); 200 | } else 201 | sprintf(status, "501 Not Implemented"); 202 | } 203 | } 204 | } else if (res == 0) { 205 | /* x-www-form-urlencoded handler */ 206 | /* Content-Type: application/x-www-form-urlencoded */ 207 | if (*method && !*formdata) { 208 | if ( !strcasecmp(method, "POST") && clength && s->inlen==clength) { 209 | pthread_mutex_lock(&s->lock); 210 | strncpy(formdata, s->inbuf, clength); 211 | s->inlen = 0; 212 | pthread_mutex_unlock(&s->lock); 213 | sprintf(status, "200 OK"); 214 | } 215 | } 216 | } 217 | 218 | if (res < 0) 219 | break; 220 | 221 | if (*status) { 222 | HTTPHeader(s, status); 223 | 224 | /* now, let's transform and copy into a standard message block */ 225 | if (!strcmp("200 OK", status) ) { 226 | URLDecode(formdata); 227 | res = ParseHTTPInput(formdata, m); 228 | return res; 229 | } else { 230 | pthread_mutex_lock(&s->lock); 231 | s->outputcomplete = 1; 232 | pthread_mutex_unlock(&s->lock); 233 | return 0; 234 | } 235 | } 236 | } 237 | return -1; 238 | } 239 | 240 | /* We do not define a _write or _onconnect method */ 241 | -------------------------------------------------------------------------------- /src/include/astmanproxy.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #ifdef __APPLE__ 22 | #include "poll-compat.h" 23 | #else 24 | #include 25 | #endif 26 | 27 | #define BUFSIZE 1024 28 | #define MAX_HEADERS 256 29 | #define MAX_LEN 1024 30 | #define MAX_STACK 1024 31 | #define MAX_STACKDATA 32768 32 | 33 | #define ATS_RESERVED 1 34 | #define ATS_UNIQUE 2 35 | #define ATS_SRCUNIQUE 4 36 | #define ATS_DSTUNIQUE 8 37 | 38 | #define PROXY_BANNER "Asterisk Call Manager Proxy" 39 | #define PROXY_SHUTDOWN "ProxyMessage: Proxy Shutting Down" 40 | #define ACTION_ID "ActionID" 41 | 42 | struct ast_server { 43 | char nickname[80]; 44 | char ast_host[40]; 45 | char ast_port[10]; 46 | char ast_user[80]; 47 | char ast_pass[80]; 48 | char ast_events[10]; 49 | int use_ssl; /* Use SSL when Connecting to Server? */ 50 | int status; /* TODO: have this mean something */ 51 | struct ast_server *next; 52 | }; 53 | 54 | struct proxy_user { 55 | char username[80]; 56 | char secret[80]; 57 | char channel[80]; 58 | char icontext[80]; 59 | char ocontext[80]; 60 | char account[80]; 61 | char server[80]; 62 | char more_events[2]; 63 | struct proxy_user *next; 64 | }; 65 | 66 | struct proxyconfig { 67 | struct ast_server *serverlist; 68 | struct proxy_user *userlist; 69 | char listen_addr[INET_ADDRSTRLEN]; 70 | int listen_port; 71 | char inputformat[80]; 72 | char outputformat[80]; 73 | int autofilter; /* enable autofiltering? */ 74 | int authrequired; /* is authentication required? */ 75 | char key[80]; 76 | char proc_user[40]; 77 | char proc_group[40]; 78 | char logfile[256]; 79 | int retryinterval; 80 | int maxretries; 81 | int asteriskwritetimeout; /* ms to wait when writing to asteriskfor ast_carefulwrite */ 82 | int clientwritetimeout; /* ms to wait when writing to client ast_carefulwrite */ 83 | int sslclhellotimeout; /* ssl client hello timeout -- how long to wait before assuming not ssl */ 84 | int acceptencryptedconnection; /* accept encrypted connections? */ 85 | int acceptunencryptedconnection; /* accept unencrypted connections? */ 86 | char certfile[256]; /* our SERVER-side SSL certificate file */ 87 | }; 88 | 89 | struct iohandler { 90 | int (*read) (); 91 | int (*write) (); 92 | int (*onconnect) (); 93 | char formatname[80]; 94 | void *dlhandle; 95 | struct iohandler *next; 96 | }; 97 | 98 | struct mstack { 99 | struct mstack *next; 100 | char uniqueid[80]; 101 | char *message; 102 | }; 103 | 104 | struct mansession { 105 | pthread_t t; 106 | pthread_mutex_t lock; 107 | struct sockaddr_in sin; 108 | int fd; 109 | char inbuf[MAX_LEN]; 110 | int inlen; 111 | struct iohandler *input; 112 | struct iohandler *output; 113 | int autofilter; 114 | int authenticated; 115 | int connected; 116 | int dead; /* Whether we are dead */ 117 | int busy; /* Whether we are busy */ 118 | int inputcomplete; /* Whether we want any more input from this session (http) */ 119 | int outputcomplete; /* Whether output to this session is done (http) */ 120 | struct ast_server *server; 121 | struct proxy_user user; 122 | char actionid[MAX_LEN]; 123 | char challenge[10]; /*! Authentication challenge */ 124 | int writetimeout; /* Timeout for ast_carefulwrite() */ 125 | struct mstack *stack; 126 | int depth; 127 | struct mansession *next; 128 | }; 129 | 130 | struct message { 131 | int hdrcount; 132 | char headers[MAX_HEADERS][MAX_LEN]; 133 | int in_command; 134 | struct mansession *session; 135 | }; 136 | 137 | struct proxyconfig pc; 138 | extern int debug; 139 | 140 | /* Common Function Prototypes */ 141 | void debugmsg (const char *, ...); 142 | const char *ast_inet_ntoa(char *buf, int bufsiz, struct in_addr ia); 143 | int AddHeader(struct message *m, const char *fmt, ...); 144 | void debugmsg (const char *fmt, ...); 145 | void logmsg (const char *fmt, ...); 146 | 147 | int StartServer(struct ast_server *srv); 148 | int WriteAsterisk(struct message *m); 149 | char *astman_get_header(struct message *m, char *var); 150 | int proxyerror_do(struct mansession *s, char *err); 151 | int get_input(struct mansession *s, char *output); 152 | int SetIOHandlers(struct mansession *s, char *ifmt, char *ofmt); 153 | void destroy_session(struct mansession *s); 154 | int ast_carefulwrite(int fd, char *s, int len, int timeoutms); 155 | extern void *SendError(struct mansession *s, char *errmsg, char *actionid); 156 | 157 | int close_sock(int socket); 158 | int ProxyChallenge(struct mansession *s, struct message *m); 159 | int ast_connect(struct mansession *a); 160 | int is_encrypt_request(int sslclhellotimeout, int fd); 161 | int saccept(int s); 162 | int get_real_fd(int fd); 163 | int client_init_secure(void); 164 | int init_secure(char *certfile); 165 | int m_send(int fd, const void *data, size_t len); 166 | int m_recv(int s, void *buf, size_t len, int flags); 167 | -------------------------------------------------------------------------------- /src/include/dlfcn-compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2002 Jorge Acereda & 3 | Peter O'Gorman 4 | 5 | Portions may be copyright others, see the AUTHORS file included with this 6 | distribution. 7 | 8 | Maintained by Peter O'Gorman 9 | 10 | Bug Reports and other queries should go to 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | */ 31 | #ifndef _DLFCN_H_ 32 | #define _DLFCN_H_ 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | #if defined (__GNUC__) && __GNUC__ > 3 39 | #define dl_restrict __restrict 40 | #else 41 | #define dl_restrict 42 | #endif 43 | 44 | #ifndef _POSIX_SOURCE 45 | /* 46 | * Structure filled in by dladdr(). 47 | */ 48 | typedef struct dl_info { 49 | const char *dli_fname; /* Pathname of shared object */ 50 | void *dli_fbase; /* Base address of shared object */ 51 | const char *dli_sname; /* Name of nearest symbol */ 52 | void *dli_saddr; /* Address of nearest symbol */ 53 | } Dl_info; 54 | 55 | extern int dladdr(const void * dl_restrict, Dl_info * dl_restrict); 56 | #endif /* ! _POSIX_SOURCE */ 57 | 58 | extern int dlclose(void * handle); 59 | extern char * dlerror(void); 60 | extern void * dlopen(const char *path, int mode); 61 | extern void * dlsym(void * dl_restrict handle, const char * dl_restrict symbol); 62 | 63 | #define RTLD_LAZY 0x1 64 | #define RTLD_NOW 0x2 65 | #define RTLD_LOCAL 0x4 66 | #define RTLD_GLOBAL 0x8 67 | 68 | #ifndef _POSIX_SOURCE 69 | #define RTLD_NOLOAD 0x10 70 | #define RTLD_NODELETE 0x80 71 | 72 | /* 73 | * Special handle arguments for dlsym(). 74 | */ 75 | #define RTLD_NEXT ((void *) -1) /* Search subsequent objects. */ 76 | #define RTLD_DEFAULT ((void *) -2) /* Use default search algorithm. */ 77 | #endif /* ! _POSIX_SOURCE */ 78 | 79 | #ifdef __cplusplus 80 | } 81 | #endif 82 | 83 | #endif /* _DLFCN_H_ */ 84 | -------------------------------------------------------------------------------- /src/include/endian.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- A telephony toolkit for Linux. 3 | * 4 | * Asterisk architecture endianess compatibility definitions 5 | * 6 | * Copyright (C) 1999 - 2005, Digium, Inc. 7 | * 8 | * Mark Spencer 9 | * 10 | * This program is free software, distributed under the terms of 11 | * the GNU Lesser General Public License. Other components of 12 | * Asterisk are distributed under The GNU General Public License 13 | * only. 14 | */ 15 | 16 | #ifndef _ASTERISK_ENDIAN_H 17 | #define _ASTERISK_ENDIAN_H 18 | 19 | /* 20 | * Autodetect system endianess 21 | */ 22 | 23 | #ifdef SOLARIS 24 | #include "solaris-compat/compat.h" 25 | #endif 26 | 27 | #ifndef __BYTE_ORDER 28 | #ifdef __linux__ 29 | #include 30 | #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) 31 | #if defined(__OpenBSD__) 32 | #include 33 | #endif /* __OpenBSD__ */ 34 | #include 35 | #define __BYTE_ORDER BYTE_ORDER 36 | #define __LITTLE_ENDIAN LITTLE_ENDIAN 37 | #define __BIG_ENDIAN BIG_ENDIAN 38 | #else 39 | #ifdef __LITTLE_ENDIAN__ 40 | #define __BYTE_ORDER __LITTLE_ENDIAN 41 | #endif /* __LITTLE_ENDIAN */ 42 | 43 | #if defined(i386) || defined(__i386__) 44 | #define __BYTE_ORDER __LITTLE_ENDIAN 45 | #endif /* defined i386 */ 46 | 47 | #if defined(sun) && defined(unix) && defined(sparc) 48 | #define __BYTE_ORDER __BIG_ENDIAN 49 | #endif /* sun unix sparc */ 50 | 51 | #endif /* linux */ 52 | 53 | #endif /* __BYTE_ORDER */ 54 | 55 | #ifndef __BYTE_ORDER 56 | #error Need to know endianess 57 | #endif /* __BYTE_ORDER */ 58 | 59 | #endif /* _ASTERISK_ENDIAN_H */ 60 | 61 | -------------------------------------------------------------------------------- /src/include/md5.h: -------------------------------------------------------------------------------- 1 | #ifndef MD5_H 2 | #define MD5_H 3 | 4 | #include 5 | 6 | struct MD5Context { 7 | uint32_t buf[4]; 8 | uint32_t bits[2]; 9 | unsigned char in[64]; 10 | }; 11 | 12 | void MD5Init(struct MD5Context *context); 13 | void MD5Update(struct MD5Context *context, unsigned char const *buf, 14 | unsigned len); 15 | void MD5Final(unsigned char digest[16], struct MD5Context *context); 16 | void MD5Transform(uint32_t buf[4], uint32_t const in[16]); 17 | 18 | #endif /* !MD5_H */ 19 | -------------------------------------------------------------------------------- /src/include/poll-compat.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------*\ 2 | $Id: poll-compat.h,v 1.1 2003/10/26 18:50:49 markster Exp $ 3 | 4 | NAME 5 | 6 | poll - select(2)-based poll() emulation function for BSD systems. 7 | 8 | SYNOPSIS 9 | #include "poll.h" 10 | 11 | struct pollfd 12 | { 13 | int fd; 14 | short events; 15 | short revents; 16 | } 17 | 18 | int poll (struct pollfd *pArray, unsigned long n_fds, int timeout) 19 | 20 | DESCRIPTION 21 | 22 | This file, and the accompanying "poll.c", implement the System V 23 | poll(2) system call for BSD systems (which typically do not provide 24 | poll()). Poll() provides a method for multiplexing input and output 25 | on multiple open file descriptors; in traditional BSD systems, that 26 | capability is provided by select(). While the semantics of select() 27 | differ from those of poll(), poll() can be readily emulated in terms 28 | of select() -- which is how this function is implemented. 29 | 30 | REFERENCES 31 | Stevens, W. Richard. Unix Network Programming. Prentice-Hall, 1990. 32 | 33 | NOTES 34 | 1. This software requires an ANSI C compiler. 35 | 36 | LICENSE 37 | 38 | This software is released under the following license: 39 | 40 | Copyright (c) 1995-2002 Brian M. Clapper 41 | All rights reserved. 42 | 43 | Redistribution and use in source and binary forms are 44 | permitted provided that: (1) source distributions retain 45 | this entire copyright notice and comment; (2) modifications 46 | made to the software are prominently mentioned, and a copy 47 | of the original software (or a pointer to its location) are 48 | included; and (3) distributions including binaries display 49 | the following acknowledgement: "This product includes 50 | software developed by Brian M. Clapper " 51 | in the documentation or other materials provided with the 52 | distribution. The name of the author may not be used to 53 | endorse or promote products derived from this software 54 | without specific prior written permission. 55 | 56 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS 57 | OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE 58 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 59 | PARTICULAR PURPOSE. 60 | 61 | Effectively, this means you can do what you want with the software 62 | except remove this notice or take advantage of the author's name. 63 | If you modify the software and redistribute your modified version, 64 | you must indicate that your version is a modification of the 65 | original, and you must provide either a pointer to or a copy of the 66 | original. 67 | \*---------------------------------------------------------------------------*/ 68 | 69 | #ifndef _POLL_EMUL_H_ 70 | #define _POLL_EMUL_H_ 71 | 72 | #define POLLIN 0x01 73 | #define POLLPRI 0x02 74 | #define POLLOUT 0x04 75 | #define POLLERR 0x08 76 | #define POLLHUP 0x10 77 | #define POLLNVAL 0x20 78 | 79 | struct pollfd 80 | { 81 | int fd; 82 | short events; 83 | short revents; 84 | }; 85 | 86 | #ifdef __cplusplus 87 | extern "C" 88 | { 89 | #endif 90 | 91 | #if (__STDC__ > 0) || defined(__cplusplus) 92 | extern int poll (struct pollfd *pArray, unsigned long n_fds, int timeout); 93 | #else 94 | extern int poll(); 95 | #endif 96 | 97 | #ifdef __cplusplus 98 | } 99 | #endif 100 | 101 | #endif /* _POLL_EMUL_H_ */ 102 | -------------------------------------------------------------------------------- /src/include/ssl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ssl_addon: Encrypts the asterisk management interface 3 | * 4 | * Copyrights: 5 | * Copyright (C) 2005-2008, Tello Corporation, Inc. 6 | * 7 | * Contributors: 8 | * Remco Treffkorn(Architect) and Mahesh Karoshi 9 | * 10 | * This program is free software, distributed under the terms of 11 | * the GNU Lesser (Library) General Public License 12 | * 13 | * Copyright on this file is disclaimed to Digium for inclusion in Asterisk 14 | */ 15 | 16 | #ifndef _SSL_ADDON_H_ 17 | #define _SSL_ADDON_H_ 18 | 19 | #include 20 | #include "astmanproxy.h" 21 | 22 | int connect_nonb(struct mansession *a); 23 | 24 | /*! \brief 25 | This data structure holds the additional SSL data needed to use the ssl functions. 26 | The negative fd is used as an index into this data structure (after processing). 27 | Choose SEC_MAX to be impossibly large for the application. 28 | */ 29 | #define SEC_MAX 16 30 | struct { 31 | int fd; 32 | SSL* ssl; 33 | } sec_channel[SEC_MAX]; 34 | 35 | /*! \brief 36 | this has to be called before any other function dealing with ssl. 37 | */ 38 | int init_secure(char* certfile); 39 | 40 | /*! \brief 41 | Returns the real fd, that is received from os, when we accept the connection. 42 | */ 43 | int get_real_fd(int fd); 44 | 45 | /*! \brief 46 | Returns the ssl structure from the fd. 47 | */ 48 | SSL *get_ssl(int fd); 49 | 50 | /*! \brief 51 | Returns the availabe security slot. This restricts the maximun number of security connection, 52 | the asterisk server can have for AMI. 53 | */ 54 | int sec_getslot(void); 55 | 56 | /*! \brief 57 | Accepts the connection, if the security is enabled it returns the negative fd. -1 is flase, -2, -3 58 | etc are ssl connections. 59 | */ 60 | int saccept(int s); 61 | 62 | /*! \brief 63 | Sends the data over secured or unsecured connections. 64 | */ 65 | int m_send(int fd, const void *data, size_t len); 66 | 67 | 68 | /*! \brief 69 | Receives the connection from either ssl or fd. 70 | */ 71 | int m_recv(int s, void *buf, size_t len, int flags); 72 | 73 | 74 | /*! \brief 75 | Needs to be called instead of close() to close a socket. 76 | It also closes the ssl meta connection. 77 | */ 78 | 79 | int close_sock(int socket); 80 | 81 | int errexit(char s[]); 82 | 83 | int is_encrypt_request(int sslclhellotimeout, int fd); 84 | #ifdef __cplusplus 85 | } 86 | #endif 87 | 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | /* Asterisk Manager Proxy 2 | Copyright (c) 2005-2008 David C. Troy 3 | 4 | This program is free software, distributed under the terms of 5 | the GNU General Public License. 6 | 7 | log.c 8 | Log & debug routines 9 | */ 10 | 11 | #include "astmanproxy.h" 12 | 13 | #define DATEFORMAT "%b %e %T" 14 | 15 | extern FILE *proxylog; 16 | extern int debug; 17 | extern pthread_mutex_t loglock; 18 | extern pthread_mutex_t debuglock; 19 | 20 | void debugmsg (const char *fmt, ...) 21 | { 22 | va_list ap; 23 | 24 | time_t t; 25 | struct tm tm; 26 | char date[80]; 27 | 28 | if (!debug) 29 | return; 30 | 31 | time(&t); 32 | localtime_r(&t, &tm); 33 | strftime(date, sizeof(date), DATEFORMAT, &tm); 34 | 35 | pthread_mutex_lock(&debuglock); 36 | va_start(ap, fmt); 37 | printf("%s: ", date); 38 | vprintf(fmt, ap); 39 | printf("\n"); 40 | va_end(ap); 41 | pthread_mutex_unlock(&debuglock); 42 | } 43 | 44 | 45 | void logmsg (const char *fmt, ...) 46 | { 47 | va_list ap; 48 | 49 | time_t t; 50 | struct tm tm; 51 | char date[80]; 52 | 53 | time(&t); 54 | localtime_r(&t, &tm); 55 | strftime(date, sizeof(date), DATEFORMAT, &tm); 56 | 57 | if (proxylog) { 58 | pthread_mutex_lock(&loglock); 59 | va_start(ap, fmt); 60 | fprintf(proxylog, "%s: ", date); 61 | vfprintf(proxylog, fmt, ap); 62 | fprintf(proxylog, "\n"); 63 | va_end(ap); 64 | fflush(proxylog); 65 | pthread_mutex_unlock(&loglock); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/md5.c: -------------------------------------------------------------------------------- 1 | /* MD5 checksum routines used for authentication. Not covered by GPL, but 2 | in the public domain as per the copyright below */ 3 | 4 | /* 5 | * This code implements the MD5 message-digest algorithm. 6 | * The algorithm is due to Ron Rivest. This code was 7 | * written by Colin Plumb in 1993, no copyright is claimed. 8 | * This code is in the public domain; do with it what you wish. 9 | * 10 | * Equivalent code is available from RSA Data Security, Inc. 11 | * This code has been tested against that, and is equivalent, 12 | * except that you don't need to include two pages of legalese 13 | * with every copy. 14 | * 15 | * To compute the message digest of a chunk of bytes, declare an 16 | * MD5Context structure, pass it to MD5Init, call MD5Update as 17 | * needed on buffers full of bytes, and then call MD5Final, which 18 | * will fill a supplied 16-byte array with the digest. 19 | */ 20 | 21 | #include "endian.h" 22 | #include "astmanproxy.h" 23 | #include "md5.h" 24 | 25 | # if __BYTE_ORDER == __BIG_ENDIAN 26 | # define HIGHFIRST 1 27 | # endif 28 | #ifndef HIGHFIRST 29 | #define byteReverse(buf, len) /* Nothing */ 30 | #else 31 | void byteReverse(unsigned char *buf, unsigned longs); 32 | 33 | #ifndef ASM_MD5 34 | /* 35 | * Note: this code is harmless on little-endian machines. 36 | */ 37 | void byteReverse(unsigned char *buf, unsigned longs) 38 | { 39 | uint32_t t; 40 | do { 41 | t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | 42 | ((unsigned) buf[1] << 8 | buf[0]); 43 | *(uint32_t *) buf = t; 44 | buf += 4; 45 | } while (--longs); 46 | } 47 | #endif 48 | #endif 49 | 50 | /* 51 | * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious 52 | * initialization constants. 53 | */ 54 | void MD5Init(struct MD5Context *ctx) 55 | { 56 | ctx->buf[0] = 0x67452301; 57 | ctx->buf[1] = 0xefcdab89; 58 | ctx->buf[2] = 0x98badcfe; 59 | ctx->buf[3] = 0x10325476; 60 | 61 | ctx->bits[0] = 0; 62 | ctx->bits[1] = 0; 63 | } 64 | 65 | /* 66 | * Update context to reflect the concatenation of another buffer full 67 | * of bytes. 68 | */ 69 | void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) 70 | { 71 | uint32_t t; 72 | 73 | /* Update bitcount */ 74 | 75 | t = ctx->bits[0]; 76 | if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) 77 | ctx->bits[1]++; /* Carry from low to high */ 78 | ctx->bits[1] += len >> 29; 79 | 80 | t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ 81 | 82 | /* Handle any leading odd-sized chunks */ 83 | 84 | if (t) { 85 | unsigned char *p = (unsigned char *) ctx->in + t; 86 | 87 | t = 64 - t; 88 | if (len < t) { 89 | memcpy(p, buf, len); 90 | return; 91 | } 92 | memcpy(p, buf, t); 93 | byteReverse(ctx->in, 16); 94 | MD5Transform(ctx->buf, (uint32_t *) ctx->in); 95 | buf += t; 96 | len -= t; 97 | } 98 | /* Process data in 64-byte chunks */ 99 | 100 | while (len >= 64) { 101 | memcpy(ctx->in, buf, 64); 102 | byteReverse(ctx->in, 16); 103 | MD5Transform(ctx->buf, (uint32_t *) ctx->in); 104 | buf += 64; 105 | len -= 64; 106 | } 107 | 108 | /* Handle any remaining bytes of data. */ 109 | 110 | memcpy(ctx->in, buf, len); 111 | } 112 | 113 | /* 114 | * Final wrapup - pad to 64-byte boundary with the bit pattern 115 | * 1 0* (64-bit count of bits processed, MSB-first) 116 | */ 117 | void MD5Final(unsigned char digest[16], struct MD5Context *ctx) 118 | { 119 | unsigned count; 120 | unsigned char *p; 121 | 122 | /* Compute number of bytes mod 64 */ 123 | count = (ctx->bits[0] >> 3) & 0x3F; 124 | 125 | /* Set the first char of padding to 0x80. This is safe since there is 126 | always at least one byte free */ 127 | p = ctx->in + count; 128 | *p++ = 0x80; 129 | 130 | /* Bytes of padding needed to make 64 bytes */ 131 | count = 64 - 1 - count; 132 | 133 | /* Pad out to 56 mod 64 */ 134 | if (count < 8) { 135 | /* Two lots of padding: Pad the first block to 64 bytes */ 136 | memset(p, 0, count); 137 | byteReverse(ctx->in, 16); 138 | MD5Transform(ctx->buf, (uint32_t *) ctx->in); 139 | 140 | /* Now fill the next block with 56 bytes */ 141 | memset(ctx->in, 0, 56); 142 | } else { 143 | /* Pad block to 56 bytes */ 144 | memset(p, 0, count - 8); 145 | } 146 | byteReverse(ctx->in, 14); 147 | 148 | /* Append length in bits and transform */ 149 | ((uint32_t *) ctx->in)[14] = ctx->bits[0]; 150 | ((uint32_t *) ctx->in)[15] = ctx->bits[1]; 151 | 152 | MD5Transform(ctx->buf, (uint32_t *) ctx->in); 153 | byteReverse((unsigned char *) ctx->buf, 4); 154 | memcpy(digest, ctx->buf, 16); 155 | memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ 156 | } 157 | 158 | #ifndef ASM_MD5 159 | 160 | /* The four core functions - F1 is optimized somewhat */ 161 | 162 | /* #define F1(x, y, z) (x & y | ~x & z) */ 163 | #define F1(x, y, z) (z ^ (x & (y ^ z))) 164 | #define F2(x, y, z) F1(z, x, y) 165 | #define F3(x, y, z) (x ^ y ^ z) 166 | #define F4(x, y, z) (y ^ (x | ~z)) 167 | 168 | /* This is the central step in the MD5 algorithm. */ 169 | #define MD5STEP(f, w, x, y, z, data, s) \ 170 | ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) 171 | 172 | /* 173 | * The core of the MD5 algorithm, this alters an existing MD5 hash to 174 | * reflect the addition of 16 longwords of new data. MD5Update blocks 175 | * the data and converts bytes into longwords for this routine. 176 | */ 177 | void MD5Transform(uint32_t buf[4], uint32_t const in[16]) 178 | { 179 | register uint32_t a, b, c, d; 180 | 181 | a = buf[0]; 182 | b = buf[1]; 183 | c = buf[2]; 184 | d = buf[3]; 185 | 186 | MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); 187 | MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); 188 | MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); 189 | MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); 190 | MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); 191 | MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); 192 | MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); 193 | MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); 194 | MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); 195 | MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); 196 | MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); 197 | MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); 198 | MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); 199 | MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); 200 | MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); 201 | MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); 202 | 203 | MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); 204 | MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); 205 | MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); 206 | MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); 207 | MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); 208 | MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); 209 | MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); 210 | MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); 211 | MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); 212 | MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); 213 | MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); 214 | MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); 215 | MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); 216 | MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); 217 | MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); 218 | MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); 219 | 220 | MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); 221 | MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); 222 | MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); 223 | MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); 224 | MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); 225 | MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); 226 | MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); 227 | MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); 228 | MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); 229 | MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); 230 | MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); 231 | MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); 232 | MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); 233 | MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); 234 | MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); 235 | MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); 236 | 237 | MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); 238 | MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); 239 | MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); 240 | MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); 241 | MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); 242 | MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); 243 | MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); 244 | MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); 245 | MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); 246 | MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); 247 | MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); 248 | MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); 249 | MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); 250 | MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); 251 | MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); 252 | MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); 253 | 254 | buf[0] += a; 255 | buf[1] += b; 256 | buf[2] += c; 257 | buf[3] += d; 258 | } 259 | 260 | #endif 261 | -------------------------------------------------------------------------------- /src/poll.c: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------*\ 2 | $Id: poll.c,v 1.1 2003/10/26 19:17:28 tholo Exp $ 3 | 4 | NAME 5 | 6 | poll - select(2)-based poll() emulation function for BSD systems. 7 | 8 | SYNOPSIS 9 | #include "poll.h" 10 | 11 | struct pollfd 12 | { 13 | int fd; 14 | short events; 15 | short revents; 16 | } 17 | 18 | int poll (struct pollfd *pArray, unsigned long n_fds, int timeout) 19 | 20 | DESCRIPTION 21 | 22 | This file, and the accompanying "poll.h", implement the System V 23 | poll(2) system call for BSD systems (which typically do not provide 24 | poll()). Poll() provides a method for multiplexing input and output 25 | on multiple open file descriptors; in traditional BSD systems, that 26 | capability is provided by select(). While the semantics of select() 27 | differ from those of poll(), poll() can be readily emulated in terms 28 | of select() -- which is how this function is implemented. 29 | 30 | REFERENCES 31 | Stevens, W. Richard. Unix Network Programming. Prentice-Hall, 1990. 32 | 33 | NOTES 34 | 1. This software requires an ANSI C compiler. 35 | 36 | LICENSE 37 | 38 | This software is released under the following license: 39 | 40 | Copyright (c) 1995-2002 Brian M. Clapper 41 | All rights reserved. 42 | 43 | Redistribution and use in source and binary forms are 44 | permitted provided that: (1) source distributions retain 45 | this entire copyright notice and comment; (2) modifications 46 | made to the software are prominently mentioned, and a copy 47 | of the original software (or a pointer to its location) are 48 | included; and (3) distributions including binaries display 49 | the following acknowledgement: "This product includes 50 | software developed by Brian M. Clapper " 51 | in the documentation or other materials provided with the 52 | distribution. The name of the author may not be used to 53 | endorse or promote products derived from this software 54 | without specific prior written permission. 55 | 56 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS 57 | OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE 58 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 59 | PARTICULAR PURPOSE. 60 | 61 | Effectively, this means you can do what you want with the software 62 | except remove this notice or take advantage of the author's name. 63 | If you modify the software and redistribute your modified version, 64 | you must indicate that your version is a modification of the 65 | original, and you must provide either a pointer to or a copy of the 66 | original. 67 | \*---------------------------------------------------------------------------*/ 68 | 69 | 70 | /*---------------------------------------------------------------------------*\ 71 | Includes 72 | \*---------------------------------------------------------------------------*/ 73 | 74 | #include /* standard Unix definitions */ 75 | #include /* system types */ 76 | #include /* time definitions */ 77 | #include /* assertion macros */ 78 | #include /* string functions */ 79 | 80 | #include "poll-compat.h" /* this package */ 81 | 82 | /*---------------------------------------------------------------------------*\ 83 | Macros 84 | \*---------------------------------------------------------------------------*/ 85 | 86 | #ifndef MAX 87 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) 88 | #endif 89 | 90 | 91 | /*---------------------------------------------------------------------------*\ 92 | Private Functions 93 | \*---------------------------------------------------------------------------*/ 94 | 95 | static int map_poll_spec 96 | #if __STDC__ > 0 97 | (struct pollfd *pArray, 98 | unsigned long n_fds, 99 | fd_set *pReadSet, 100 | fd_set *pWriteSet, 101 | fd_set *pExceptSet) 102 | #else 103 | (pArray, n_fds, pReadSet, pWriteSet, pExceptSet) 104 | struct pollfd *pArray; 105 | unsigned long n_fds; 106 | fd_set *pReadSet; 107 | fd_set *pWriteSet; 108 | fd_set *pExceptSet; 109 | #endif 110 | { 111 | register unsigned long i; /* loop control */ 112 | register struct pollfd *pCur; /* current array element */ 113 | register int max_fd = -1; /* return value */ 114 | 115 | /* 116 | Map the poll() structures into the file descriptor sets required 117 | by select(). 118 | */ 119 | for (i = 0, pCur = pArray; i < n_fds; i++, pCur++) 120 | { 121 | /* Skip any bad FDs in the array. */ 122 | 123 | if (pCur->fd < 0) 124 | continue; 125 | 126 | if (pCur->events & POLLIN) 127 | { 128 | /* "Input Ready" notification desired. */ 129 | FD_SET (pCur->fd, pReadSet); 130 | } 131 | 132 | if (pCur->events & POLLOUT) 133 | { 134 | /* "Output Possible" notification desired. */ 135 | FD_SET (pCur->fd, pWriteSet); 136 | } 137 | 138 | if (pCur->events & POLLPRI) 139 | { 140 | /* 141 | "Exception Occurred" notification desired. (Exceptions 142 | include out of band data. 143 | */ 144 | FD_SET (pCur->fd, pExceptSet); 145 | } 146 | 147 | max_fd = MAX (max_fd, pCur->fd); 148 | } 149 | 150 | return max_fd; 151 | } 152 | 153 | static struct timeval *map_timeout 154 | #if __STDC__ > 0 155 | (int poll_timeout, struct timeval *pSelTimeout) 156 | #else 157 | (poll_timeout, pSelTimeout) 158 | int poll_timeout; 159 | struct timeval *pSelTimeout; 160 | #endif 161 | { 162 | struct timeval *pResult; 163 | 164 | /* 165 | Map the poll() timeout value into a select() timeout. The possible 166 | values of the poll() timeout value, and their meanings, are: 167 | 168 | VALUE MEANING 169 | 170 | -1 wait indefinitely (until signal occurs) 171 | 0 return immediately, don't block 172 | >0 wait specified number of milliseconds 173 | 174 | select() uses a "struct timeval", which specifies the timeout in 175 | seconds and microseconds, so the milliseconds value has to be mapped 176 | accordingly. 177 | */ 178 | 179 | assert (pSelTimeout != (struct timeval *) NULL); 180 | 181 | switch (poll_timeout) 182 | { 183 | case -1: 184 | /* 185 | A NULL timeout structure tells select() to wait indefinitely. 186 | */ 187 | pResult = (struct timeval *) NULL; 188 | break; 189 | 190 | case 0: 191 | /* 192 | "Return immediately" (test) is specified by all zeros in 193 | a timeval structure. 194 | */ 195 | pSelTimeout->tv_sec = 0; 196 | pSelTimeout->tv_usec = 0; 197 | pResult = pSelTimeout; 198 | break; 199 | 200 | default: 201 | /* Wait the specified number of milliseconds. */ 202 | pSelTimeout->tv_sec = poll_timeout / 1000; /* get seconds */ 203 | poll_timeout %= 1000; /* remove seconds */ 204 | pSelTimeout->tv_usec = poll_timeout * 1000; /* get microseconds */ 205 | pResult = pSelTimeout; 206 | break; 207 | } 208 | 209 | 210 | return pResult; 211 | } 212 | 213 | static void map_select_results 214 | #if __STDC__ > 0 215 | (struct pollfd *pArray, 216 | unsigned long n_fds, 217 | fd_set *pReadSet, 218 | fd_set *pWriteSet, 219 | fd_set *pExceptSet) 220 | #else 221 | (pArray, n_fds, pReadSet, pWriteSet, pExceptSet) 222 | struct pollfd *pArray; 223 | unsigned long n_fds; 224 | fd_set *pReadSet; 225 | fd_set *pWriteSet; 226 | fd_set *pExceptSet; 227 | #endif 228 | { 229 | register unsigned long i; /* loop control */ 230 | register struct pollfd *pCur; /* current array element */ 231 | 232 | for (i = 0, pCur = pArray; i < n_fds; i++, pCur++) 233 | { 234 | /* Skip any bad FDs in the array. */ 235 | 236 | if (pCur->fd < 0) 237 | continue; 238 | 239 | /* Exception events take priority over input events. */ 240 | 241 | pCur->revents = 0; 242 | if (FD_ISSET (pCur->fd, pExceptSet)) 243 | pCur->revents |= POLLPRI; 244 | 245 | else if (FD_ISSET (pCur->fd, pReadSet)) 246 | pCur->revents |= POLLIN; 247 | 248 | if (FD_ISSET (pCur->fd, pWriteSet)) 249 | pCur->revents |= POLLOUT; 250 | } 251 | 252 | return; 253 | } 254 | 255 | /*---------------------------------------------------------------------------*\ 256 | Public Functions 257 | \*---------------------------------------------------------------------------*/ 258 | 259 | int poll 260 | 261 | #if __STDC__ > 0 262 | (struct pollfd *pArray, unsigned long n_fds, int timeout) 263 | #else 264 | (pArray, n_fds, timeout) 265 | struct pollfd *pArray; 266 | unsigned long n_fds; 267 | int timeout; 268 | #endif 269 | 270 | { 271 | fd_set read_descs; /* input file descs */ 272 | fd_set write_descs; /* output file descs */ 273 | fd_set except_descs; /* exception descs */ 274 | struct timeval stime; /* select() timeout value */ 275 | int ready_descriptors; /* function result */ 276 | int max_fd; /* maximum fd value */ 277 | struct timeval *pTimeout; /* actually passed */ 278 | 279 | FD_ZERO (&read_descs); 280 | FD_ZERO (&write_descs); 281 | FD_ZERO (&except_descs); 282 | 283 | assert (pArray != (struct pollfd *) NULL); 284 | 285 | /* Map the poll() file descriptor list in the select() data structures. */ 286 | 287 | max_fd = map_poll_spec (pArray, n_fds, 288 | &read_descs, &write_descs, &except_descs); 289 | 290 | /* Map the poll() timeout value in the select() timeout structure. */ 291 | 292 | pTimeout = map_timeout (timeout, &stime); 293 | 294 | /* Make the select() call. */ 295 | 296 | ready_descriptors = select (max_fd + 1, &read_descs, &write_descs, 297 | &except_descs, pTimeout); 298 | 299 | if (ready_descriptors >= 0) 300 | { 301 | map_select_results (pArray, n_fds, 302 | &read_descs, &write_descs, &except_descs); 303 | } 304 | 305 | return ready_descriptors; 306 | } 307 | -------------------------------------------------------------------------------- /src/proxyfunc.c: -------------------------------------------------------------------------------- 1 | /* Asterisk Manager Proxy 2 | Copyright (c) 2005-2008 David C. Troy 3 | 4 | This program is free software, distributed under the terms of 5 | the GNU General Public License. 6 | 7 | proxyfunc.c 8 | Functions specific to the manager proxy, not found in standard Asterisk AMI 9 | */ 10 | 11 | #include "astmanproxy.h" 12 | #include "md5.h" 13 | 14 | extern struct mansession *sessions; 15 | extern struct iohandler *iohandlers; 16 | extern pthread_mutex_t serverlock; 17 | extern pthread_mutex_t userslock; 18 | 19 | void *ProxyListIOHandlers(struct mansession *s) { 20 | struct message m; 21 | struct iohandler *i; 22 | 23 | memset(&m, 0, sizeof(struct message)); 24 | AddHeader(&m, "ProxyResponse: Success"); 25 | 26 | i = iohandlers; 27 | while (i && (m.hdrcount < MAX_HEADERS - 1) ) { 28 | if (i->read) 29 | AddHeader(&m, "InputHandler: %s", i->formatname); 30 | if (i->write) 31 | AddHeader(&m, "OutputHandler: %s", i->formatname); 32 | i = i->next; 33 | } 34 | 35 | s->output->write(s, &m); 36 | return 0; 37 | } 38 | 39 | void *ProxyListSessions(struct mansession *s) { 40 | struct message m; 41 | struct mansession *c; 42 | char iabuf[INET_ADDRSTRLEN]; 43 | 44 | memset(&m, 0, sizeof(struct message)); 45 | AddHeader(&m, "ProxyResponse: Success"); 46 | 47 | c = sessions; 48 | while (c && (m.hdrcount < MAX_HEADERS - 1) ) { 49 | if (!c->server) { 50 | AddHeader(&m, "ProxyClientSession: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr), c->actionid); 51 | AddHeader(&m, "ProxyClientInputHandler: %s", c->input->formatname); 52 | AddHeader(&m, "ProxyClientOutputHandler: %s", c->output->formatname); 53 | } else 54 | AddHeader(&m, "ProxyServerSession: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr)); 55 | c = c->next; 56 | } 57 | s->output->write(s, &m); 58 | return 0; 59 | } 60 | 61 | void *ProxySetOutputFormat(struct mansession *s, struct message *m) { 62 | struct message mo; 63 | char *value; 64 | 65 | value = astman_get_header(m, "OutputFormat"); 66 | SetIOHandlers(s, s->input->formatname, value); 67 | 68 | memset(&mo, 0, sizeof(struct message)); 69 | AddHeader(&mo, "ProxyResponse: Success"); 70 | AddHeader(&mo, "OutputFormat: %s", s->output->formatname ); 71 | 72 | s->output->write(s, &mo); 73 | 74 | return 0; 75 | } 76 | 77 | int ProxyChallenge(struct mansession *s, struct message *m) { 78 | struct message mo; 79 | char *actionid; 80 | 81 | actionid = astman_get_header(m, "ActionID"); 82 | if ( strcasecmp("MD5", astman_get_header(m, "AuthType")) ) { 83 | SendError(s, "Must specify AuthType", actionid); 84 | return 1; 85 | } 86 | 87 | if (!*s->challenge) 88 | snprintf(s->challenge, sizeof(s->challenge), "%d", rand()); 89 | 90 | memset(&mo, 0, sizeof(struct message)); 91 | AddHeader(&mo, "Response: Success"); 92 | AddHeader(&mo, "Challenge: %s", s->challenge); 93 | if( actionid && strlen(actionid) ) 94 | AddHeader(&mo, "ActionID: %s", actionid); 95 | 96 | s->output->write(s, &mo); 97 | return 0; 98 | } 99 | 100 | void *ProxySetAutoFilter(struct mansession *s, struct message *m) { 101 | struct message mo; 102 | char *value; 103 | int i; 104 | 105 | value = astman_get_header(m, "AutoFilter"); 106 | if ( !strcasecmp(value, "on") ) 107 | i = 1; 108 | else if ( !strcasecmp(value, "unique") ) 109 | i = 2; 110 | else 111 | i = 0; 112 | pthread_mutex_lock(&s->lock); 113 | s->autofilter = i; 114 | if( i == 2 ) 115 | snprintf(s->actionid, MAX_LEN - 20, "amp%d-", s->fd); 116 | else 117 | s->actionid[0] = '\0'; 118 | pthread_mutex_unlock(&s->lock); 119 | 120 | memset(&mo, 0, sizeof(struct message)); 121 | AddHeader(&mo, "ProxyResponse: Success"); 122 | AddHeader(&mo, "AutoFilter: %d", s->autofilter); 123 | 124 | s->output->write(s, &mo); 125 | 126 | return 0; 127 | } 128 | 129 | int AuthMD5(char *key, char *challenge, char *password) { 130 | int x; 131 | int len=0; 132 | char md5key[256] = ""; 133 | struct MD5Context md5; 134 | unsigned char digest[16]; 135 | 136 | if (!*key || !*challenge || !*password ) 137 | return 1; 138 | 139 | if (debug) 140 | debugmsg("MD5 password=%s, challenge=%s", password, challenge); 141 | 142 | MD5Init(&md5); 143 | MD5Update(&md5, (unsigned char *) challenge, strlen(challenge)); 144 | MD5Update(&md5, (unsigned char *) password, strlen(password)); 145 | MD5Final(digest, &md5); 146 | for (x=0;x<16;x++) 147 | len += sprintf(md5key + len, "%2.2x", digest[x]); 148 | if( debug ) { 149 | debugmsg("MD5 computed=%s, received=%s", md5key, key); 150 | } 151 | if (!strcmp(md5key, key)) 152 | return 0; 153 | else 154 | return 1; 155 | } 156 | 157 | void *ProxyLogin(struct mansession *s, struct message *m) { 158 | struct message mo; 159 | struct proxy_user *pu; 160 | char *user, *secret, *key, *actionid; 161 | 162 | user = astman_get_header(m, "Username"); 163 | secret = astman_get_header(m, "Secret"); 164 | key = astman_get_header(m, "Key"); 165 | actionid = astman_get_header(m, "ActionID"); 166 | 167 | memset(&mo, 0, sizeof(struct message)); 168 | if( actionid && strlen(actionid) > 0 ) 169 | AddHeader(&mo, "ActionID: %s", actionid); 170 | if( debug ) 171 | debugmsg("Login attempt as: %s/%s", user, secret); 172 | 173 | pthread_mutex_lock(&userslock); 174 | pu = pc.userlist; 175 | while( pu ) { 176 | if ( !strcmp(user, pu->username) ) { 177 | if (!AuthMD5(key, s->challenge, pu->secret) || !strcmp(secret, pu->secret) ) { 178 | AddHeader(&mo, "Response: Success"); 179 | AddHeader(&mo, "Message: Authentication accepted"); 180 | s->output->write(s, &mo); 181 | pthread_mutex_lock(&s->lock); 182 | s->authenticated = 1; 183 | strcpy(s->user.channel, pu->channel); 184 | strcpy(s->user.icontext, pu->icontext); 185 | strcpy(s->user.ocontext, pu->ocontext); 186 | strcpy(s->user.account, pu->account); 187 | strcpy(s->user.server, pu->server); 188 | strcpy(s->user.more_events, pu->more_events); 189 | pthread_mutex_unlock(&s->lock); 190 | if( debug ) 191 | debugmsg("Login as: %s", user); 192 | break; 193 | } 194 | } 195 | pu = pu->next; 196 | } 197 | pthread_mutex_unlock(&userslock); 198 | 199 | if( !pu ) { 200 | SendError(s, "Authentication failed", actionid); 201 | pthread_mutex_lock(&s->lock); 202 | s->authenticated = 0; 203 | pthread_mutex_unlock(&s->lock); 204 | if( debug ) 205 | debugmsg("Login failed as: %s/%s", user, secret); 206 | } 207 | 208 | return 0; 209 | } 210 | 211 | void *ProxyLogoff(struct mansession *s) { 212 | struct message m; 213 | 214 | memset(&m, 0, sizeof(struct message)); 215 | AddHeader(&m, "Goodbye: Y'all come back now, y'hear?"); 216 | 217 | s->output->write(s, &m); 218 | 219 | destroy_session(s); 220 | if (debug) 221 | debugmsg("Client logged off - exiting thread"); 222 | pthread_exit(NULL); 223 | return 0; 224 | } 225 | 226 | int ProxyAddServer(struct mansession *s, struct message *m) { 227 | struct message mo; 228 | struct ast_server *srv; 229 | int res = 0; 230 | 231 | /* malloc ourselves a server credentials structure */ 232 | srv = malloc(sizeof(struct ast_server)); 233 | if ( !srv ) { 234 | fprintf(stderr, "Failed to allocate server credentials: %s\n", strerror(errno)); 235 | exit(1); 236 | } 237 | 238 | memset(srv, 0, sizeof(struct ast_server) ); 239 | memset(&mo, 0, sizeof(struct message)); 240 | strcpy(srv->ast_host, astman_get_header(m, "Server")); 241 | strcpy(srv->ast_user, astman_get_header(m, "Username")); 242 | strcpy(srv->ast_pass, astman_get_header(m, "Secret")); 243 | strcpy(srv->ast_port, astman_get_header(m, "Port")); 244 | strcpy(srv->ast_events, astman_get_header(m, "Events")); 245 | 246 | if (*srv->ast_host && *srv->ast_user && *srv->ast_pass && *srv->ast_port && *srv->ast_events) { 247 | pthread_mutex_lock(&serverlock); 248 | srv->next = pc.serverlist; 249 | pc.serverlist = srv; 250 | pthread_mutex_unlock(&serverlock); 251 | res = StartServer(srv); 252 | } else 253 | res = 1; 254 | 255 | if (res) { 256 | AddHeader(&mo, "ProxyResponse: Failure"); 257 | AddHeader(&mo, "Message: Could not add %s", srv->ast_host); 258 | } else { 259 | AddHeader(&mo, "ProxyResponse: Success"); 260 | AddHeader(&mo, "Message: Added %s", srv->ast_host); 261 | } 262 | 263 | s->output->write(s, &mo); 264 | return 0; 265 | } 266 | 267 | int ProxyDropServer(struct mansession *s, struct message *m) { 268 | struct message mo; 269 | struct mansession *srv; 270 | char *value; 271 | int res; 272 | 273 | memset(&mo, 0, sizeof(struct message)); 274 | value = astman_get_header(m, "Server"); 275 | srv = sessions; 276 | while (*value && srv) { 277 | if (srv->server && !strcmp(srv->server->ast_host, value)) 278 | break; 279 | srv = srv->next; 280 | } 281 | 282 | if (srv) { 283 | destroy_session(srv); 284 | debugmsg("Dropping Server %s", value); 285 | AddHeader(&mo, "ProxyResponse: Success"); 286 | AddHeader(&mo, "Message: Dropped %s", value); 287 | res = 0; 288 | } else { 289 | debugmsg("Failed to Drop Server %s -- not found", value); 290 | AddHeader(&mo, "ProxyResponse: Failure"); 291 | AddHeader(&mo, "Message: Cannot Drop Server %s, Does Not Exist", value); 292 | res = 1; 293 | } 294 | 295 | s->output->write(s, &mo); 296 | return res; 297 | } 298 | 299 | void *ProxyListServers(struct mansession *s) { 300 | struct message m; 301 | struct mansession *c; 302 | char iabuf[INET_ADDRSTRLEN]; 303 | 304 | memset(&m, 0, sizeof(struct message)); 305 | AddHeader(&m, "ProxyResponse: Success"); 306 | 307 | c = sessions; 308 | while (c) { 309 | if (c->server) { 310 | AddHeader(&m, "ProxyListServer I: %s H: %s U: %s P: %s E: %s ", 311 | ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr), 312 | c->server->ast_host, c->server->ast_user, 313 | c->server->ast_port, c->server->ast_events); 314 | } 315 | 316 | c = c->next; 317 | } 318 | s->output->write(s, &m); 319 | return 0; 320 | } 321 | 322 | 323 | void *proxyaction_do(char *proxyaction, struct message *m, struct mansession *s) 324 | { 325 | if (!strcasecmp(proxyaction,"SetOutputFormat")) 326 | ProxySetOutputFormat(s, m); 327 | else if (!strcasecmp(proxyaction,"SetAutoFilter")) 328 | ProxySetAutoFilter(s, m); 329 | else if (!strcasecmp(proxyaction,"ListSessions")) 330 | ProxyListSessions(s); 331 | else if (!strcasecmp(proxyaction,"AddServer")) 332 | ProxyAddServer(s, m); 333 | else if (!strcasecmp(proxyaction,"DropServer")) 334 | ProxyDropServer(s, m); 335 | else if (!strcasecmp(proxyaction,"ListServers")) 336 | ProxyListServers(s); 337 | else if (!strcasecmp(proxyaction,"ListIOHandlers")) 338 | ProxyListIOHandlers(s); 339 | else if (!strcasecmp(proxyaction,"Logoff")) 340 | ProxyLogoff(s); 341 | else 342 | proxyerror_do(s, "Invalid Proxy Action"); 343 | 344 | return 0; 345 | } 346 | 347 | int proxyerror_do(struct mansession *s, char *err) 348 | { 349 | struct message mo; 350 | 351 | memset(&mo, 0, sizeof(struct message)); 352 | AddHeader(&mo, "ProxyResponse: Error"); 353 | AddHeader(&mo, "Message: %s", err); 354 | 355 | s->output->write(s, &mo); 356 | 357 | return 0; 358 | } 359 | 360 | /* [do_]AddToStack - Stores an event in a stack for later repetition. 361 | indexed on UniqueID. 362 | If SrcUniqueID / DestUniqueID are present, store against both. 363 | If a record already exists, do nothing. 364 | withbody = 1, saves a copy of whole message (server). 365 | withbody = 0, saves just the key (client). 366 | */ 367 | int do_AddToStack(char *uniqueid, struct message *m, struct mansession *s, int withbody) 368 | { 369 | struct mstack *prev; 370 | struct mstack *t; 371 | 372 | pthread_mutex_lock(&s->lock); 373 | prev = NULL; 374 | t = s->stack; 375 | 376 | while( t ) { 377 | if( !strncmp( t->uniqueid, uniqueid, sizeof(t->uniqueid) ) ) 378 | { 379 | pthread_mutex_unlock(&s->lock); 380 | return 0; 381 | } 382 | prev = t; 383 | t = t->next; 384 | } 385 | if( s->depth >= MAX_STACK ) { 386 | struct mstack *newtop; 387 | 388 | newtop = s->stack->next; 389 | if( s->stack->message ) 390 | free( s->stack->message ); 391 | free( s->stack ); 392 | s->stack = newtop; 393 | s->depth--; 394 | } 395 | if( (t = malloc(sizeof(struct mstack))) ) { 396 | memset(t, 0, sizeof(struct mstack)); 397 | strncpy( t->uniqueid, uniqueid, sizeof(t->uniqueid) ); 398 | s->depth++; 399 | if( prev ) 400 | prev->next = t; 401 | else 402 | s->stack = t; 403 | if( withbody ) { 404 | // Save the message, in a reduced form to save memory... 405 | int m_size; 406 | int i, j; 407 | m_size = 1; 408 | j = 0; 409 | for( i = 0; i < m->hdrcount; i++ ) { 410 | m_size += strlen(m->headers[i])+1; 411 | } 412 | if( m_size < MAX_STACKDATA && (t->message = malloc(m_size)) ) { 413 | memset(t->message, 0, m_size); 414 | for( i = 0; i < m->hdrcount; i++ ) { 415 | strncpy( t->message + j, m->headers[i], m_size - j ); 416 | *(t->message + j + strlen(m->headers[i])) = '\n'; 417 | j += strlen(m->headers[i]) + 1; 418 | } 419 | } 420 | } 421 | if( debug ) { 422 | debugmsg("Added uniqueid: %s to %s stack", uniqueid, withbody?"server":"client"); 423 | if( t->message) 424 | debugmsg("Cached message: %s", t->message); 425 | } 426 | } 427 | pthread_mutex_unlock(&s->lock); 428 | return 1; 429 | } 430 | int AddToStack(struct message *m, struct mansession *s, int withbody) 431 | { 432 | char *uniqueid; 433 | int ret, absent; 434 | 435 | ret=0; 436 | absent=0; 437 | 438 | uniqueid = astman_get_header(m, "Uniqueid"); 439 | if( uniqueid[0] != '\0' ) { 440 | if( do_AddToStack(uniqueid, m, s, withbody) ) 441 | ret |= ATS_UNIQUE; 442 | } else 443 | absent++; 444 | 445 | uniqueid = astman_get_header(m, "SrcUniqueID"); 446 | if( uniqueid[0] != '\0' ) { 447 | if( do_AddToStack(uniqueid, m, s, withbody) ) 448 | ret |= ATS_SRCUNIQUE; 449 | } else 450 | absent++; 451 | 452 | uniqueid = astman_get_header(m, "DestUniqueID"); 453 | if( uniqueid[0] != '\0' ) { 454 | if( do_AddToStack(uniqueid, m, s, withbody) ) 455 | ret |= ATS_DSTUNIQUE; 456 | } else 457 | absent++; 458 | 459 | if( s->user.more_events[0] != '\0' && absent == 3 ) 460 | return 1; // Want more/anonymous events 461 | return ret; 462 | } 463 | 464 | 465 | /* DelFromStack - Removes an item from the stack based on the UniqueID field. 466 | */ 467 | void DelFromStack(struct message *m, struct mansession *s) 468 | { 469 | char *uniqueid; 470 | struct mstack *prev; 471 | struct mstack *t; 472 | 473 | uniqueid = astman_get_header(m, "Uniqueid"); 474 | if( uniqueid[0] == '\0' ) 475 | return; 476 | 477 | pthread_mutex_lock(&s->lock); 478 | prev = NULL; 479 | t = s->stack; 480 | 481 | while( t ) { 482 | if( !strncmp( t->uniqueid, uniqueid, sizeof(t->uniqueid) ) ) 483 | { 484 | if( t->message ) 485 | free( t->message ); 486 | if( prev ) 487 | prev->next = t->next; 488 | else 489 | s->stack = t->next; 490 | free( t ); 491 | s->depth--; 492 | if( debug ) 493 | debugmsg("Removed uniqueid: %s from stack", uniqueid); 494 | break; 495 | } 496 | prev = t; 497 | t = t->next; 498 | } 499 | pthread_mutex_unlock(&s->lock); 500 | } 501 | 502 | /* FreeStack - Removes all items from stack. 503 | */ 504 | void FreeStack(struct mansession *s) 505 | { 506 | struct mstack *t, *n; 507 | 508 | pthread_mutex_lock(&s->lock); 509 | t = s->stack; 510 | 511 | while( t ) { 512 | n = t->next; // Grab next entry BEFORE we free the slot 513 | if( t->message ) 514 | free( t->message ); 515 | free( t ); 516 | t = n; 517 | s->depth--; 518 | } 519 | s->stack = NULL; 520 | if( debug && s->depth > 0 ) 521 | debugmsg("ALERT! Stack may have leaked %d slots!!!", s->depth); 522 | if( debug ) 523 | debugmsg("Freed entire stack."); 524 | pthread_mutex_unlock(&s->lock); 525 | } 526 | 527 | /* IsInStack - If the message has a UniqueID, and it is in the stack... 528 | */ 529 | int IsInStack(char* uniqueid, struct mansession *s) 530 | { 531 | struct mstack *t; 532 | 533 | pthread_mutex_lock(&s->lock); 534 | t = s->stack; 535 | 536 | while( t ) { 537 | if( !strncmp( t->uniqueid, uniqueid, sizeof(t->uniqueid) ) ) 538 | { 539 | pthread_mutex_unlock(&s->lock); 540 | return 1; 541 | } 542 | t = t->next; 543 | } 544 | pthread_mutex_unlock(&s->lock); 545 | return 0; 546 | } 547 | 548 | /* ResendFromStack - We want to resend a cached message from the stack please... 549 | * Look for "uniqueid" in cache of session "s", and reconstruct into message "m" 550 | */ 551 | void ResendFromStack(char* uniqueid, struct mansession *s, struct message *m) 552 | { 553 | struct mstack *t; 554 | 555 | if( !m ) 556 | return; 557 | 558 | if( debug ) 559 | debugmsg("ResendFromStack: %s", uniqueid); 560 | 561 | pthread_mutex_lock(&s->lock); 562 | t = s->stack; 563 | 564 | while( t ) { 565 | if( !strncmp( t->uniqueid, uniqueid, sizeof(t->uniqueid) ) ) 566 | { 567 | // Got message, pull from cache. 568 | int i, h, j; 569 | for( i=0,h=0,j=0; imessage) && i < MAX_STACKDATA-1 && h < MAX_HEADERS; i++ ) { 570 | if( t->message[i] == '\n' || i-j >= 80 ) { 571 | strncpy( m->headers[h], t->message + j, i-j ); 572 | m->headers[h][79] = '\0'; 573 | j = i + 1; 574 | if( debug ) 575 | debugmsg("remade: %s", m->headers[h]); 576 | h++; 577 | } 578 | } 579 | m->hdrcount = h; 580 | pthread_mutex_unlock(&s->lock); 581 | return; 582 | } 583 | t = t->next; 584 | } 585 | pthread_mutex_unlock(&s->lock); 586 | return; 587 | } 588 | 589 | int ValidateAction(struct message *m, struct mansession *s, int inbound) { 590 | char *channel, *channel1, *channel2; 591 | char *context; 592 | char *uchannel; 593 | char *ucontext; 594 | char *action; 595 | char *actionid; 596 | char *event; 597 | char *response; 598 | char *account; 599 | char *uniqueid; 600 | 601 | if( pc.authrequired && !s->authenticated ) 602 | return 0; 603 | 604 | if( inbound ) // Inbound to client from server 605 | ucontext = s->user.icontext; 606 | else // Outbound from client to server 607 | ucontext = s->user.ocontext; 608 | uchannel = s->user.channel; 609 | 610 | // There is no filering, so just return quickly. 611 | if( uchannel[0] == '\0' && ucontext[0] == '\0' && s->user.account[0] == '\0' ) 612 | return 1; 613 | 614 | event = astman_get_header(m, "Event"); 615 | uniqueid = astman_get_header(m, "Uniqueid"); 616 | if( uniqueid[0] != '\0' && IsInStack(uniqueid, s) ) { 617 | if( debug ) 618 | debugmsg("Message passed (uniqueid): %s already allowed", uniqueid); 619 | if( !strcasecmp( event, "Hangup" ) ) 620 | DelFromStack(m, s); 621 | return 1; 622 | } 623 | uniqueid = astman_get_header(m, "Uniqueid1"); 624 | if( uniqueid[0] != '\0' && IsInStack(uniqueid, s) ) { 625 | if( debug ) 626 | debugmsg("Message passed (uniqueid1): %s already allowed", uniqueid); 627 | if( !strcasecmp( event, "Hangup" ) ) 628 | DelFromStack(m, s); 629 | return 1; 630 | } 631 | uniqueid = astman_get_header(m, "Uniqueid2"); 632 | if( uniqueid[0] != '\0' && IsInStack(uniqueid, s) ) { 633 | if( debug ) 634 | debugmsg("Message passed (uniqueid2): %s already allowed", uniqueid); 635 | if( !strcasecmp( event, "Hangup" ) ) 636 | DelFromStack(m, s); 637 | return 1; 638 | } 639 | 640 | // Response packets rarely have any of the following fields included, so 641 | // we will return a response if the ActionID matches our last known ActionID 642 | response = astman_get_header(m, "Response"); 643 | actionid = astman_get_header(m, ACTION_ID); 644 | if( response[0] != '\0' && actionid[0] != '\0' && !strcmp(actionid, s->actionid) ) { 645 | if (s->autofilter < 2 && !strcmp(actionid, s->actionid)) 646 | return 1; 647 | else if ( !strncmp(actionid, s->actionid, strlen(s->actionid)) ) 648 | return 1; 649 | } 650 | 651 | if( uchannel[0] != '\0' ) { 652 | channel = astman_get_header(m, "Channel"); 653 | if( channel[0] != '\0' ) { // We have a Channel: header, so filter on it. 654 | if( strncasecmp( channel, uchannel, strlen(uchannel) ) ) { 655 | if( debug ) 656 | debugmsg("Message filtered (chan): %s != %s", channel, uchannel); 657 | return 0; 658 | } 659 | } else { // No Channel: header, what about Channel1: or Channel2: ? 660 | channel1 = astman_get_header(m, "Channel1"); 661 | channel2 = astman_get_header(m, "Channel2"); 662 | if( channel1[0] != '\0' || channel2[0] != '\0' ) { 663 | if( !(strncasecmp( channel1, uchannel, strlen(uchannel) ) == 0 || 664 | strncasecmp( channel2, uchannel, strlen(uchannel) ) == 0) ) { 665 | if( debug ) 666 | debugmsg("Message filtered (chan1/2): %s/%s != %s", channel1, channel2, uchannel); 667 | return 0; 668 | } 669 | } else { // No? What about Source: and Destination: 670 | channel1 = astman_get_header(m, "Source"); 671 | channel2 = astman_get_header(m, "Destination"); 672 | if( channel1[0] != '\0' || channel2[0] != '\0' ) { 673 | if( !(strncasecmp( channel1, uchannel, strlen(uchannel) ) == 0 || 674 | strncasecmp( channel2, uchannel, strlen(uchannel) ) == 0) ) { 675 | if( debug ) 676 | debugmsg("Message filtered (src/dst chan): %s/%s != %s", channel1, channel2, uchannel); 677 | return 0; 678 | } 679 | } 680 | } 681 | } 682 | } 683 | 684 | context = astman_get_header(m, "Context"); 685 | if( context[0] != '\0' && ucontext[0] != '\0' ) 686 | if( strcasecmp( context, ucontext ) ) { 687 | if( debug ) 688 | debugmsg("Message filtered (ctxt): %s != %s", context, ucontext); 689 | return 0; 690 | } 691 | 692 | if( s->user.account[0] != '\0' ) { 693 | action = astman_get_header(m, "Action"); 694 | account = astman_get_header(m, "Account"); 695 | if( !strcasecmp( action, "Originate" ) ) { 696 | if( debug ) 697 | debugmsg("Got Originate. Account: %s, setting to: %s", account, s->user.account); 698 | if( account[0] == '\0' ) 699 | AddHeader(m, "Account: %s", s->user.account); 700 | else 701 | strcpy(account, s->user.account); 702 | } else if( account[0] != '\0' ) { 703 | if( debug ) 704 | debugmsg("Got Account: %s, setting to: %s", account, s->user.account); 705 | strcpy(account, s->user.account); 706 | } 707 | } 708 | 709 | if( inbound ) { 710 | int res; 711 | res = AddToStack(m, s, 0); 712 | if( debug > 5 ) 713 | debugmsg("AddToStack returned %d", res); 714 | return res; 715 | } 716 | return 1; 717 | } 718 | 719 | void *SendError(struct mansession *s, char *errmsg, char *actionid) { 720 | struct message m; 721 | 722 | memset(&m, 0, sizeof(struct message)); 723 | AddHeader(&m, "Response: Error"); 724 | AddHeader(&m, "Message: %s", errmsg); 725 | if( actionid && strlen(actionid) ) 726 | AddHeader(&m, "ActionID: %s", actionid); 727 | 728 | s->output->write(s, &m); 729 | 730 | return 0; 731 | } 732 | -------------------------------------------------------------------------------- /src/ssl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2008, Tello Corporation, Inc. 5 | * 6 | * Remco Treffkorn(Architect) and Mahesh Karoshi(Senior Software Developer) 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | 18 | We use negative file descriptors for secure channels. The file descriptor 19 | -1 is reseved for errors. -2 to -... are secure file descriptors. 0 to ... 20 | are regular file descriptors. 21 | 22 | NOTE: Commonly error checks for routines returning fd's are done with (value<0). 23 | You must check for (value==-1) instead, since all other negative fd's now 24 | are valid fd's. 25 | */ 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "ssl.h" 41 | 42 | SSL_CTX *sctx; 43 | SSL_CTX *cctx; 44 | static long rec_bytes; 45 | static long sent_bytes; 46 | static int ssl_initialized; 47 | 48 | 49 | /*! \brief this has to be called before any other function dealing with ssl. 50 | Initializes all the ssl related stuff here. */ 51 | int init_secure(char *certfile) 52 | { 53 | SSL_METHOD *meth; 54 | 55 | SSLeay_add_ssl_algorithms(); 56 | SSL_load_error_strings(); 57 | 58 | /* server init */ 59 | meth = SSLv23_server_method(); 60 | sctx = SSL_CTX_new(meth); 61 | 62 | if (!sctx) { 63 | return errexit("Failed to create a server ssl context!"); 64 | } 65 | 66 | if (SSL_CTX_use_certificate_file(sctx, certfile, SSL_FILETYPE_PEM) <= 0) { 67 | return errexit("Failed to use the certificate file!"); 68 | } 69 | 70 | if (SSL_CTX_use_PrivateKey_file(sctx, certfile, SSL_FILETYPE_PEM) <= 0) { 71 | return errexit("Failed to use the key file!\n"); 72 | } 73 | 74 | if (!SSL_CTX_check_private_key(sctx)) { 75 | return errexit("Private key does not match the certificate public key"); 76 | } 77 | ssl_initialized = 1; 78 | return 0; 79 | } 80 | 81 | 82 | /* Initializes all the client-side ssl related stuff here. 83 | */ 84 | int client_init_secure(void) 85 | { 86 | SSL_METHOD *meth; 87 | 88 | /* client init */ 89 | SSLeay_add_ssl_algorithms(); 90 | meth = SSLv23_client_method(); 91 | SSL_load_error_strings(); 92 | cctx = SSL_CTX_new (meth); 93 | 94 | if (!cctx) 95 | debugmsg("Failed to create a client ssl context!"); 96 | else 97 | debugmsg("Client SSL Context Initialized"); 98 | return 0; 99 | } 100 | 101 | /*! \brief Takes the negative ssl fd and returns the positive fd recieved from the os. 102 | * It goes through arrray of fixed maximum number of secured channels. 103 | */ 104 | int get_real_fd(int fd) 105 | { 106 | if (fd<-1) { 107 | fd = -fd - 2; 108 | if (fd>=0 && fd =0 && fd 0) 218 | rec_bytes += ret; 219 | 220 | if (debug && s<-1) 221 | debugmsg("Received %d bytes from SSL socket", ret); 222 | return ret; 223 | } 224 | 225 | 226 | /*! \brief 227 | Needs to be called instead of close() to close a socket. 228 | It also closes the SSL meta connection. 229 | */ 230 | 231 | int close_sock(int socket) 232 | { 233 | int ret=0; 234 | SSL* ssl = NULL; 235 | 236 | if (socket < -1) { 237 | socket = - socket - 2; 238 | 239 | ssl = sec_channel[socket].ssl; 240 | sec_channel[socket].ssl = NULL; 241 | socket = sec_channel[socket].fd; 242 | } 243 | 244 | ret= close(get_real_fd(socket)); 245 | 246 | if (ssl) 247 | SSL_free (ssl); 248 | 249 | return(ret); 250 | } 251 | 252 | /*! \brief This process cannot continue without fixing this error. 253 | */ 254 | int errexit(char s[]) 255 | { 256 | debugmsg("SSL critical error: %s", s); 257 | return -1; 258 | } 259 | 260 | /*! \brief Checks whether the client is requesting an ssl encrypted connection or not. If its encrypted 261 | * request we expect "Client Hello" in the beginning of the message and ssl version 2. 262 | * This can be verified by checking buf[0x02], buf[0x03] and buf[0x04]. If the contents are 263 | * 0x01, 0x00, 0x02, then its an ssl packet with content "Client Hello", "SSL version 2". 264 | * For SSL version 3, we might need to check for 0x01, 0x00, 0x03. 265 | * 266 | */ 267 | int is_encrypt_request(int sslclhellotimeout, int fd) 268 | { 269 | fd_set listeners; 270 | struct timeval tv; 271 | char buf[1024]; 272 | int ready_fdescriptors; 273 | int ret; 274 | 275 | tv.tv_sec = 0; 276 | tv.tv_usec = sslclhellotimeout * 1000; 277 | 278 | FD_ZERO(&listeners); 279 | FD_SET(fd, &listeners); 280 | 281 | ready_fdescriptors = select (fd + 1, &listeners, NULL, NULL, &tv); 282 | 283 | if (ready_fdescriptors < 0 ) { 284 | debugmsg("is_encrypt_request: select returned error, This should not happen:"); 285 | return 0; 286 | } else if (ready_fdescriptors == 0) { 287 | return 0; 288 | } 289 | ret = recv(fd, buf, 100, MSG_PEEK); 290 | if(ret > 0) { 291 | /* check for sslv3 or tls*/ 292 | if ((buf[0x00] == 0x16) && (buf[0x01] == 0x03) && 293 | /* for tls buf[0x02] = 0x01 and ssl v3 buf[0x02] = 0x02 */ 294 | ((buf[0x02] == 0x00) || (buf[0x02] == 0x01))) { 295 | if (debug) 296 | debugmsg("Received a SSL request"); 297 | return 1; 298 | /* check for sslv23_client_method */ 299 | } else if ((buf[0x02] == 0x01) && (buf[0x03] == 0x03) && (buf[0x04] == 0x01)) { 300 | if (debug) 301 | debugmsg("Received a SSL request for SSLv23_client_method()"); 302 | return 1; 303 | } 304 | /* check for sslv2 and return -1 */ 305 | else if ((buf[0x02] == 0x01) && (buf[0x03] == 0x00) && (buf[0x04] == 0x02)) { 306 | if (debug) 307 | debugmsg("Received a SSLv2 request()"); 308 | return -1; 309 | } 310 | } 311 | return 0; 312 | } 313 | 314 | 315 | /* Connects to an asterisk server either plain or SSL as appropriate 316 | */ 317 | int ast_connect(struct mansession *a) { 318 | int s, err=-1, fd; 319 | SSL* ssl; 320 | 321 | fd = connect_nonb(a); 322 | if ( fd < 0 ) 323 | return -1; 324 | 325 | if (a->server->use_ssl) { 326 | debugmsg("initiating ssl connection"); 327 | if ((s=sec_getslot())!=-1) { /* find a slot for the ssl handle */ 328 | sec_channel[s].fd = fd; /* remember the real fd */ 329 | 330 | if((ssl=SSL_new(cctx))) { /* get a new ssl */ 331 | sec_channel[s].ssl = ssl; 332 | SSL_set_fd(ssl, fd); /* and attach the real fd */ 333 | err = SSL_connect(ssl); /* now try and connect */ 334 | } else 335 | debugmsg("couldn't create ssl client context"); 336 | fd = -(s+2); /* offset by two and negate */ 337 | /* this tells us it is a ssl fd */ 338 | } else 339 | debugmsg("couldn't get SSL slot!"); 340 | 341 | if (err==-1) { 342 | close_sock(fd); /* that frees the ssl too */ 343 | fd = -1; 344 | } 345 | } 346 | 347 | debugmsg("returning ast_connect with %d", fd); 348 | pthread_mutex_lock(&a->lock); 349 | a->fd = fd; 350 | pthread_mutex_unlock(&a->lock); 351 | 352 | return fd; 353 | } 354 | 355 | int connect_nonb(struct mansession *a) 356 | { 357 | int flags, n, error; 358 | socklen_t len; 359 | fd_set rset, wset; 360 | struct timeval tval; 361 | int nsec = 1, sockfd; 362 | 363 | sockfd = get_real_fd(a->fd); 364 | 365 | flags = fcntl(sockfd, F_GETFL, 0); 366 | fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); 367 | 368 | error = 0; 369 | if ( (n = connect(sockfd, (struct sockaddr *) &a->sin, sizeof(a->sin)) ) < 0 ) { 370 | /* TODO: This seems like the nine pound hammer to me... */ 371 | /* perhaps something a bit more elegant; errno seems to change too */ 372 | if (errno == EISCONN || errno == 103 || errno==111) { 373 | debugmsg("connect_nonb: error %d, closing old fd and grabbing a new one...", errno); 374 | /* looks like our old socket died, let's round up a new one and try again */ 375 | close_sock(a->fd); 376 | pthread_mutex_lock(&a->lock); 377 | a->fd = socket(AF_INET, SOCK_STREAM, 0); 378 | pthread_mutex_unlock(&a->lock); 379 | return(-1); 380 | } 381 | if (errno != EINPROGRESS) 382 | return(-1); 383 | } 384 | 385 | /* Do whatever we want while the connect is taking place. */ 386 | 387 | if (n == 0) 388 | goto done; /* connect completed immediately */ 389 | 390 | FD_ZERO(&rset); 391 | FD_SET(sockfd, &rset); 392 | wset = rset; 393 | tval.tv_sec = nsec; 394 | tval.tv_usec = 0; 395 | 396 | if ( (n = select(sockfd+1, &rset, &wset, NULL, 397 | nsec ? &tval : NULL)) == 0) { 398 | /*close(sockfd);*/ /* we want to retry */ 399 | errno = ETIMEDOUT; 400 | return(-1); 401 | } 402 | 403 | if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { 404 | len = sizeof(error); 405 | if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) 406 | return(-1); /* Solaris pending error */ 407 | } else { 408 | /*err_quit("select error: sockfd not set");*/ 409 | logmsg("select error: sockfd not set"); 410 | return(-1); 411 | } 412 | 413 | done: 414 | fcntl(sockfd, F_SETFL, flags); /* restore file status flags */ 415 | 416 | if (error) { 417 | /* close(sockfd); */ /* disable for now, we want to retry... */ 418 | errno = error; 419 | return(-1); 420 | } 421 | return(sockfd); 422 | } 423 | -------------------------------------------------------------------------------- /src/standard.c: -------------------------------------------------------------------------------- 1 | /* Asterisk Manager Proxy 2 | Copyright (c) 2005-2008 David C. Troy 3 | 4 | This program is free software, distributed under the terms of 5 | the GNU General Public License. 6 | 7 | standard.c 8 | Standard I/O Handler 9 | */ 10 | 11 | #include "astmanproxy.h" 12 | 13 | extern struct mansession *sessions; 14 | 15 | /* Return a fully formed message block to session_do for processing */ 16 | int _read(struct mansession *s, struct message *m) { 17 | int res; 18 | 19 | for (;;) { 20 | res = get_input(s, m->headers[m->hdrcount]); 21 | 22 | if (strstr(m->headers[m->hdrcount], "--END COMMAND--")) { 23 | if (debug) debugmsg("Found END COMMAND"); 24 | m->in_command = 0; 25 | } 26 | if (strstr(m->headers[m->hdrcount], "Response: Follows")) { 27 | if (debug) debugmsg("Found Response Follows"); 28 | m->in_command = 1; 29 | } 30 | if (res > 0) { 31 | if (!m->in_command && *(m->headers[m->hdrcount]) == '\0' ) { 32 | break; 33 | } else if (m->hdrcount < MAX_HEADERS - 1) { 34 | m->hdrcount++; 35 | } else { 36 | m->in_command = 0; // reset when block full 37 | } 38 | } else if (res < 0) 39 | break; 40 | } 41 | 42 | return res; 43 | } 44 | 45 | int _write(struct mansession *s, struct message *m) { 46 | int i; 47 | 48 | pthread_mutex_lock(&s->lock); 49 | for (i=0; ihdrcount; i++) { 50 | if( ! strlen(m->headers[i]) ) 51 | continue; 52 | ast_carefulwrite(s->fd, m->headers[i], strlen(m->headers[i]) , s->writetimeout); 53 | ast_carefulwrite(s->fd, "\r\n", 2, s->writetimeout); 54 | } 55 | ast_carefulwrite(s->fd, "\r\n", 2, s->writetimeout); 56 | pthread_mutex_unlock(&s->lock); 57 | 58 | return 0; 59 | } 60 | 61 | int _onconnect(struct mansession *s, struct message *m) { 62 | 63 | char banner[100]; 64 | 65 | sprintf(banner, "%s/%s\r\n", PROXY_BANNER, PROXY_VERSION); 66 | pthread_mutex_lock(&s->lock); 67 | ast_carefulwrite(s->fd, banner, strlen(banner), s->writetimeout); 68 | pthread_mutex_unlock(&s->lock); 69 | 70 | return 0; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /src/xml.c: -------------------------------------------------------------------------------- 1 | /* Asterisk Manager Proxy 2 | Copyright (c) 2005-2008 David C. Troy 3 | 4 | This program is free software, distributed under the terms of 5 | the GNU General Public License. 6 | 7 | xml.c 8 | XML I/O Handler 9 | */ 10 | 11 | #include "astmanproxy.h" 12 | 13 | #define XML_UNPARSED "UnparsedText" 14 | #define XML_BEGIN_INPUT "" 15 | #define XML_END_INPUT "" 16 | 17 | #define XML_SERVERTAG "AsteriskManagerOutput" 18 | #define XML_PROXYTAG "AsteriskManagerProxyOutput" 19 | 20 | void xml_quote_string(char *s, char *o); 21 | int ParseXMLInput(char *xb, struct message *m); 22 | 23 | int _read(struct mansession *s, struct message *m) { 24 | 25 | /* Note: No single line may be longer than MAX_LEN/s->inbuf, as per get_input */ 26 | /* No XML Input may be longer than BUFSIZE */ 27 | 28 | char line[MAX_LEN], xmlbuf[BUFSIZE]; 29 | int res; 30 | 31 | /* first let's read the whole xml block into our buffer */ 32 | memset(xmlbuf, 0, sizeof xmlbuf); 33 | for (;;) { 34 | memset(line, 0, sizeof line); 35 | res = get_input(s, line); 36 | 37 | if (res > 0) { 38 | if (*line == '\0' ) { 39 | break; 40 | } else if (strlen(xmlbuf) < (BUFSIZE - strlen(line)) ) 41 | strcat(xmlbuf, line); 42 | } else if (res < 0) 43 | return res; 44 | } 45 | 46 | /* now, let's transform and copy into a standard message block */ 47 | debugmsg("Got xml: %s", xmlbuf); 48 | res = ParseXMLInput(xmlbuf, m); 49 | 50 | if (res < 0) 51 | proxyerror_do(s, "Invalid XML Input"); 52 | 53 | /* Return res>0 to process block, return res<0 to kill client, res=0, continue */ 54 | return res; 55 | } 56 | 57 | void *setdoctag(char *tag, struct mansession *s) { 58 | 59 | /* if message came from a server, say so; otherwise it must be from proxy */ 60 | /* right now there is no such thing as client<->client comms */ 61 | if (s && s->server) 62 | strcpy(tag, XML_SERVERTAG); 63 | else 64 | strcpy(tag, XML_PROXYTAG); 65 | 66 | return 0; 67 | } 68 | 69 | int _write(struct mansession *s, struct message *m) { 70 | int i; 71 | char buf[BUFSIZE], outstring[MAX_LEN*3], xmlescaped[MAX_LEN*3], xmldoctag[MAX_LEN]; 72 | char *dpos, *lpos; 73 | 74 | setdoctag(xmldoctag, m->session); 75 | sprintf(buf, "<%s>\r\n", xmldoctag); 76 | 77 | pthread_mutex_lock(&s->lock); 78 | ast_carefulwrite(s->fd, buf, strlen(buf), s->writetimeout); 79 | 80 | for (i=0; ihdrcount; i++) { 81 | memset(xmlescaped, 0, sizeof xmlescaped); 82 | xml_quote_string(m->headers[i], xmlescaped); 83 | lpos = xmlescaped; 84 | dpos = strstr(lpos, ": "); 85 | if (dpos && *(lpos)!= ' ' && strlen(xmlescaped)<30 ) { 86 | strcpy(outstring, " <"); 87 | strncat(outstring, lpos, dpos-lpos); 88 | strcat(outstring, " Value=\""); 89 | strncat(outstring, dpos+2, strlen(dpos)-2); 90 | strcat(outstring, "\"/>\r\n"); 91 | } else 92 | sprintf(outstring, " <%s Value=\"%s\"/>\r\n", XML_UNPARSED, lpos); 93 | ast_carefulwrite(s->fd, outstring, strlen(outstring), s->writetimeout); 94 | } 95 | sprintf(buf, "\r\n\r\n", xmldoctag); 96 | ast_carefulwrite(s->fd, buf, strlen(buf), s->writetimeout); 97 | pthread_mutex_unlock(&s->lock); 98 | 99 | return 0; 100 | } 101 | 102 | /* Takes a single manager header line and converts xml entities */ 103 | void xml_quote_string(char *s, char *o) { 104 | 105 | char *c; 106 | c = s; 107 | 108 | do { 109 | if (*c == '<') 110 | strcat(o, "<"); 111 | else if (*c == '>') 112 | strcat(o, ">"); 113 | else if (*c == '&') 114 | strcat(o, "&"); 115 | else if (*c == '"') 116 | strcat(o, """); 117 | else if (*c == '\n') 118 | strcat(o, " "); 119 | else 120 | strncat(o, c, 1); 121 | } while (*(c++)); 122 | 123 | return; 124 | } 125 | 126 | int ParseXMLInput(char *xb, struct message *m) { 127 | char *b, *e, *bt, *et, tag[MAX_LEN], *i; 128 | int res = 0; 129 | 130 | /* just an empty block; go home */ 131 | if ( !(*xb) ) 132 | return 0; 133 | 134 | /* initialize message block */ 135 | memset(m, 0, sizeof(struct message) ); 136 | 137 | b = strstr(xb, XML_BEGIN_INPUT); 138 | e = strstr(xb, XML_END_INPUT); 139 | if (b && e) { 140 | bt = strstr((char *)(b + strlen(XML_BEGIN_INPUT) + 1), "<"); 141 | while (bt < e) { 142 | et = strstr(bt+1, "<"); 143 | memset(tag, 0, sizeof tag); 144 | strncpy(tag, bt, (et-bt) ); 145 | bt = et; 146 | 147 | strncpy( m->headers[m->hdrcount], tag+1, strstr(tag+1," ")-(tag+1) ); 148 | strcat(m->headers[m->hdrcount], ": "); 149 | i = strstr(tag+1, "\"") + 1; 150 | strncat( m->headers[m->hdrcount], i, strstr(i, "\"") - i ); 151 | debugmsg("parsed: %s", m->headers[m->hdrcount]); 152 | m->hdrcount++; 153 | } 154 | res = 1; 155 | } else 156 | res = -1; 157 | 158 | return res; 159 | } 160 | --------------------------------------------------------------------------------