3proxy developer reference
$Id: devref.rtf,v 1.4 2008/01/24 08:44:57 vlad Exp $

1.	Understanding Internal 3proxy structure

3proxy is implemented as multithread application. Server model is implemented as “one connection – one thread”. It means, for every client connection new thread is created. This model is effective enough under Windows, because it allows it avoid thread creation on asynchronous operations, yet under most POSIX systems this model can not be considered as most efficient. It’s planned for (very far in future) release to implement more efficient model, where single thread can serve few clients.

1.1	main thread:

3proxy begins with main thread. This thread parses configuration file and starts main loop.  During configuration file parsing struct extaparam conf; structure is filled and service threads are started.

Main loop cycle takes approximately 1 second and does these tasks:
-	re-reads configuration file, if necessary
-	performs scheduled tasks
-	monitors files (‘monitor’ command), approx. once in a minute
-	rotates main logfile
-	dumps counters to file, approx. once in a minute
-	performs termination, if required

It’s guaranteed every configuration and schedule command is executed from the same thread.
Main thread is implemented in 3proxy.c

1.2	service thread

Service threads are started immediately, than service command (e.g. ‘proxy’ or ‘socks’) are found during configuration file parsing. Each command creates new thread. Thread does these tasks:
-	parses service command arguments and fills ‘struct srvparam srv’ structure with service configuration and ‘struct clientparam defparam’ structure with default client configuration
-	initializes filters (filter_open)
-	creates and initializes listening service socket
-	enters into service loop
-	terminates filters (filter_close)

service loop:
-	checks for configuration reload (approximately every second), thread exits if configuration reloaded or 3proxy is in terminating state.
-	accepts client connection and creates ‘struct clientparam newparam’ structure with client configuration
-	creates/checks client filters (filter_client)
-	creates client thread with newly created ‘struct clientparam newparam’

service threads are implemented in proxymain.c

Please note: struct clientparam is freed and filter_clear is executed from different (client) thread.

1.3	client thread

Client threads are started from service thread. Client thread:
-	reads client request (except portmappers) with authentication information and request headers (if any).
-	filters request (if any) with  filter_request
-	filters headers (if any) with filter_header_cli
-	performs authentication and authorization
-	established connection with server
-	sends request to server
-	filters server headers (if any)
-	maps client end server sockets to transmit data between client and server
-	logs request. Global counters are also updated on this operation
-	clears client filters (filter_clear)
-	frees struct clientparam data

in some point client thread may loop to process few client requests from the same connection (e.g. HTTP ‘established’ connection in ‘proxy’).

Socket mapping does:
-	caches data in internal client and server buffers
-	delays data transmit to limit bandwidth
-	performs data filtering (filter_data_cli / filter_data_srv)

client threads are implemented in proxy.c, socks.c, pop3p.c etc.

2.	Hacking into 3proxy code with plugins

2.1 What is 3proxy plugin

3proxy plugin is any dynamic/shared library. There is no specific requirement for plugin, actually you can load any dynamic library with ‘plugin’ command. No linking with any libraries are required. However, to interoperate with 3proxy dynamic library must have an export function 3proxy may call to pass the structure with required information.

typedef int (*PLUGINFUNC) (struct pluginlink *pluginlink, int argc, char** argv);

struct pluginlink is a structure with export information, explained later, argc and argv are argument counter and array of arguments of “plugin” command. Plugin should report it’s status with integer return value. 0 is success, positive value indicates non-recoverable error, 3proxy do not parse rest of configuration and enters into termination state, negative value indicates recoverable value, 3proxy logs warning (if possible). In case of C++, all 3proxy functions/structures must be extern “C”.

All 3proxy structures/functions descriptions are located in structures.h

2.2 Understanding pluginlink structure

Because there is no linking between 3proxy and plugin, all 3proxy functions and structures are passed with pluginlink structure. Pluginlink is actually a collection of pointers to 3proxy internal structures and functions. Because pluginlink is constantly extending, you should see it’s definitions in structures.h.

most important are:

struct symbol symbols;
“symbols” is a kind of name/value  export table, made as a list. It can be used by plugins to exchange information and functions between plugins, e.g. to export functions from one plugin to another, where pluginlink is useless, because it’s static. It’s quite simple:

struct symbol {
	struct symbol *next;
	char * name;
	void * value;
};

name – is a name of function or structure
value – is it’s value.

use pluginlink->fundbyname function to lookup, e.g.

anotherplugindata = pluginlink->findbyname(“anotherplugindata”);

To export something from your plugin, add your structure to this list.

struct extparam *conf;
pointer to conf structure, it holds all current 3proxy configuration

2.3 How to get control within plugin

There are few points you can get control for your plugin, after it’s loaded with ‘plugin’ command.

	2.3.1 Adding configuration command processor with struct command structure

A list of configuration file command, available from 3proxy.cfg is extendable.  Each command is  defined by struct commands:

struct commands {
	struct commands *next;
	char * command;
	int (* handler)(int argc, unsigned char ** argv);
	int minargs;
	int maxargs;	
};

struct commands *next - next element in list
char * command – command name
int (* handler)(int argc, unsigned char ** argv) – command handler. It’s called than ‘command’ is found in configuration files, argc is a number of arguments, counting command itself, argv is array of arguments.
minargs – minimum number of arguments command support (>= 1)
maxargs – maximum number of arguments command support, 0 means infinity.

Handler return value of 0 indicates command is successfully processed. Positive return value indicates non-recoverable error, 3proxy enters termination state. Negative value indicates 3proxy to continue to process command list, it makes it possible to set few handlers for the same command.

A list of the command is pointed by pluginlinks->commandhandlers; you must insert you command after first one (do not replace pluginlink->commandhandlers). It’s guaranteed at least 1 dummy command is always present.

Example:

int mycommandhandler(int argc, unsigned char **argv);
struct commands mycommand;

mycommand.command = “mycommand”;
mycommand.handler = mycommandhandler;
mycommand.intargs = 1;
mycommand.intargs = 2;
mycommand.next = pluginslinks->commandhandlers->next;
pluginslink->commandhandlers->next = &mycommand;

Adds processor for “mycommand” command with zero on one arguments.

Adding configuration command is useful, if your plugin expects configuration data.

	2.3.2 Adding authentication method with struct auth

3proxy supports authentication and authorization. Authentication process determines user account (for example by username and password), authorization checks, if user account has a right to access given resource and optionally establishes a connection, if required.

‘auth’ command combines both authentication and authorization method. It’s extandable with struct auth list:

struct auth {
	struct auth *next;
	AUTHFUNC authenticate;
	AUTHFUNC authorize;
	char * desc;
};

char * desc – name of authentication/authorization method
authenticate – name of authentication function
authorize – name of authorization function

pluginlink->authfuncs points to list of authenticataction structures. Like above, new structure must be inserted after fiest one (or to the end of the list).

First, authentication is called, if authentication indicates OK status (return value 0), authorization is called. Normally, ‘checkACL’ (pluginlink->checkACL) is called as authorization function to check user’s request matches to standard allow/deny rules. If for some reason you need to avoid this check, you should call pluginlink->alwaysauth to do some dirty job, like establishing outgoing connection.

typedef int (*AUTHFUNC)(struct clientparam * param);

is both authentication and authorization function. ‘struct clientparam’ holds all information about client connection, including username (param->username) and password (param->password).

Return value of 0 indicates successful authentication/authorization, 1 and 3 – authorization failed (access denied), use 3 in case you want to indicate access is explicitly denied and 3 in case there is no matching rule. 4,5,6,7,8 – authentication failed (e.g. username/password do not match). 4 indicates username does not present in request and must be requested, if possible. 5 indicates username found in request can not be found in user’s database/list, 6,7,8 – username does not match password for different authentication types. 10 – user exceeded some limits, e.g. traffic. You may use some different code to indicate internal problems.

Example:

int myauthfunc(struct clientparam *param);
struct auth myauth;

myauth.desc = “myauth”;
myauth.authenticate = myauthfunc;
myauth.authorize = pluginlink->checkACL;
myauth->next = pluginlink->authfuncs->next;
pluginlink->authfuncs->next = &myauth;

Installs “myauthfunc” as authentication function. There is no need to add ‘auth’ command processor for new authentication type, it’s processed by standard ‘auth’ command processor.

	2.3.3 Adding scheduled functions

Scheduled functions are described by this structure:

typedef enum {NONE, MINUTELY, HOURLY, DAILY, WEEKLY, MONTHLY, ANNUALLY, NEVER} ROTATION;

struct schedule {
	struct schedule *next;
	ROTATION type;
	void *data;
	int (*function)(void *);
	time_t start_time;
};

int (*function)(void *) – scheduled function
void *data – this pointer will be passed as an argument to scheduled functions
ROTATION type – defines how often function is called (once in a minute, hour, etc). 
start_time – time to begin using of scheduled function

Scheduled functions are called every ‘type’ interval after start_time and also on reloading configuration and going to termination state.

Schedule function return value of 1 means function must be removed from the schedule. 3proxy doesn’t free struct schedule.

Schedule list can be empty. Pointer to schedule is pointed by struct schedule ** schedule; in pluginlink.

Example:

int myschedfunc(void * data);
struct schedule myschedule;

myschedule.data = “somethinghere”;
myschedule.function = myschedfunc;
myschedule.type = MINUTELY;
myschedule.starttime = 0;
myschedule.next = *pluginlink->schedule;
*pluginlink->schedule = myschedule;

NOTE: time_t is different for different compilers. Make sure to compile plugin and 3proxy with same compiler.

	2.3.4 Filters API

3proxy has filters API, you can use, to process client request and data flowing through proxy. It should be noted, that currently 3proxy doesn’t provide filters with any useful data conversion, so, it’s filter’s task to find data in data flow. In case filter modifies some data, it’s filter’s task again to assure that everything’s fine. If you know some filtering API like MILTER, you will find 3proxy filters very same.

typedef enum {
	PASS,
	CONTINUE,
	HANDLED,
	REJECT,
	REMOVE
} FILTER_ACTION;

typedef	void*	 	FILTER_OPEN(void * idata, struct srvparam * param);
typedef	FILTER_ACTION 	FILTER_CLIENT(void *fo, struct clientparam * param, void** fc);
typedef	FILTER_ACTION	FILTER_BUFFER(void *fc, struct clientparam * param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p);
typedef	void		FILTER_CLOSE(void *fo);

struct filter {
	struct filter * next;
	char * instance;
	void * data;
	FILTER_OPEN *filter_open;
	FILTER_CLIENT *filter_client;
	FILTER_BUFFER *filter_request;
	FILTER_BUFFER *filter_header_cli;
	FILTER_BUFFER *filter_header_srv;
	FILTER_BUFFER *filter_data_cli;
	FILTER_BUFFER *filter_data_srv;
	FILTER_CLOSE *filter_clear;
	FILTER_CLOSE *filter_close;
};

char * instance – is some instance identifier. You can use it to find required filter in the list. 3proxy itself doesn’t use this field.

void * data – this parameter is passed to filter_open function.
The rest are filtering functions. Section 1 explains where and then each filter is called. data should not be NULL.

filter_open must always be defined, if you want filter to be ever used. It’s called then new service is created and is given “data” from struct filter and struct srvparam (parsed service configuration). If filter_open for some filter returns NULL, filter will not be used for this service. Non-NULL return value will be used as “fo” parameter for every call to filter_client.

filter_client is called upon client connect (before any data is sent/received). It’s good place to filter client by IP (and is not good place to filter it by hostname, because this operation takes a long time, 3proxy will not be able to accept new connection). fo is a data pointer received from filter_open, param – newly created clientparam structure, fc is return parameter filter_open must initialize, it will be used as an argument to FILTER_BUFFER functions. PASS return value means this filter will be used for this client request. CONTINUE says to install filters. On different values client connection is closed and no client thread is created.

filter_request, filter_header_cli, filter_header_srv, filter_data_cli, filter_data_srv are used to process request and data received from client and server.

char ** buf_p is a pointer to current buffer, int * bufsize_p is a pointer to it’s size. In case you change some data and it doesn’t fit to current buffer, you may allocate new buffer (with pluginlink->myalloc), copy data from old buffer, free old buffer (with pluginlink->myfree) and set new values for *buf_p and *bufsize_p.

int * offset_p offset of the new data in the buffer, int * length_p length of all data in the buffer. You should filter only (*length_p - *offset_p) characters starting from (*buf_p + *offset_p).

filter_clear is called for each successfule filter_client and should be used to free allocated resources
filter_close is called for each successful filter_open for the same reason

An example of filter API usage you can find in PCREPlugin (see plugins/ PCREPlugin/ pcre_plugin.c).

Note: if param->nooverwritefilter is set for FILTER_BUFFER functions, filter may change data in the buffer, but must not change data length. This flag may be set, if data size if already known and is sent to the client. 

	2.3.5 Replacing log functions, traffic counting functions, bandwidth limitation functions

Log, traffic and bandwidth function can be directly replaced in any over place. All functions may be replaced in conf (pluginlink->conf->logfunc, pluginlink->conf->bandlimfunc, pluginlink->conf->trafcountfunc).  In this case, these functions will be used for services started after the changes are made. logfunc may also be changed for struct srvparam (e.g. within filter_open), bandlimfunc and trafcountfunc may be changed in struct clientparam for every client individually (e.g. within filter_client).

typedef void (*LOGFUNC)(struct clientparam * param, const unsigned char * test);
typedef void (*TRAFCOUNTFUNC)(struct clientparam * param); 
typedef unsigned (*BANDLIMFUNC)(struct clientparam * param, unsigned nbytesin, unsigned nbytesout);

struct clientparam * param – information about client request
char * text - text string (e.g. request)
nbytesin, nbytesout – number of bytes received from / send to server. bandlimfunc returns delay in milliseconds.