# The Routing Engine

In a previous section we discussed how routing part of a config file gets translated into binary representation. In this section, we will discuss how the binary representation is used during message processing.

Upon a SIP message receipt, the server performs some basic sanity checks and converts the message into sip_msg structure. After that the Routing Engine will start processing the message.

The routing engine can be found in file `action.c`.

The main function is `run_actions`. The function accepts two parameters. The first parameter is list of actions to be processed (Remember, the config file gets translated into array of linked lists. Each linked list in the array represents one "route" part of the config file). The second parameter is sip_msg structure representing the message to be processed.

Upon a receipt of a request, the linked list representing the main route part will be processed so the first parameter will be `rlist[0]`. (The linked list of main route part is always at index 0).

The function will then sequentially call `do_action` function for each element of the linked list. Return value of the function is important. If the function returns 0, processing of the list will be stopped. By returning 0 a command can indicate that processing of the message should be stopped and the message will be dropped.

Modules may export so-called on_break handlers. on_break handler is a function, that will be called when processing of the linked-list is interrupted (ret == 0). All such handlers will be called when processing of the linked-list is finished and ret == 0.

## `do_action` Function

`do_action` function is core of the routing engine. There is a big `switch` statement. Each case of the statements is one command handled by the server core itself.

The following commands are handled by the SER core itself: `drop`, `forward`, `send`, `log`, `append_branch`, `len_gt`, `setflag`, `resetflag`, `isflagset`, `setavpflag`, `resetavpflag`, `isavpflagset`, `error`, `route`, `exec`, `revert_uri`, `set_host`, `set_hostport`, `set_user`, `set_userpass`, `set_port`, `set_uri`, `prefix`, `strip`, `if`, `module`.

Each of the commands is represented by a case statement in the switch. (For example, if you are interested in implementation of `drop` command, look at "case DROP_T:" statement in the function.

The respective commands will be described now.

- `drop` - This command is very simple, it simply returns 0 which will result in abortion of processing of the request. No other commands after `drop` will be executed.

- `forward` - The function will forward the message further. The message will be either forwarded to the Request URI of the message or to IP or host given as parameter.

  In the first case, host in the Request URI must be converted into corresponding IP address. Function `mk_proxy` converts hostname to corresponding IP address. The message is then sent out using `forward_request` function.

  In the second case, hostname was converted to IP address in fixup i.e. immediately after the config file was compiled into its binary representation. The first parameter is pointer to proxy structure created in the fixup and therefore we only need to call `forward_request` here to forward the message further.

- `send` - This functions sends the message to a third-party host. The message will be sent out as is - i.e. without Request URI and Via altering.

  Hostname or IP address of the third-party host is specified as a parameter of the function.

  The message will be sent out using `udp_send` directly.

- `log` - The message given as a parameter will be logged using system logger. It can be either `syslog` or `stderr` (depends on configuration). The message is logged using `LOG` which is a macro defined in `dprint.h` header file.

- `append_branch` - Append a new URI for forking.

  More than one destinations may be associated with a single SIP request. If the server was configured so, it will use all the destinations and fork the request.

  The server keeps an array of all destinations, that should be used when forking. The array and related functions can be found in file `dset.c`. There is function `append_branch` which adds a new destination to the set.

  This command simply calls `append_branch` function and adds a new destination to the destination set.

- `len_gt` - The command accepts one number as a parameter. It then compares the number with length of the message. If the message length is greater or equal then the number then 1 will be returned otherwise the function returns -1.

- `setflag` - Sets a flag in the message. The command simply calls `setflags` function that will set the flag. Fore more information see file `flag.c`.

- `resetflag` - Same as command `setflag` - only resetflag will be called instead of setflag.

- `isflagset` - Test if the flag is set or not.

- `setavpflag(avp, flag_id)` - Sets a flag in the AVP(s). The command simply set custom flag of AVP. The flags may be used in script using `isavpflagset` or in a module to perform specific operation on marked AVPs. Flag identifier must be declared via avpflags statement.

- `resetavpflag(avp, flag_id)` - Same as command `setavpflag` - only resetavpflag will be called instead of setavpflag.

- `isavpflagset(avp, flag_id)` - Test if the avp flag is set or not.

- `error` - Log a message with NOTICE log level.

- `route` - Execute another route statement.

  As we have mentioned already, there can be more than one route statement in the config file. One of them is main (without number), the other are additional. This command makes it possible to execute an additional route statement.

  The command accepts one parameter which is route statement number. First sanity checks over the parameter will be performed. If the checks passed, function `run_actions` will be called. The function accepts two parameters. The first one is linked list to execute, the second one is sip_msg structure representing the message to be processed.

  As you might remember, each route statement was compiled into linked list of commands to be executed and head of the linked list was stored in `rlist` array. For example, head of linked list representing route statement with number 4 will be stored at position 4 in the array (position 0 is reserved for the main route statement).

  So the command will simply call `run_actions(rlist[a->p1.number], msg)` and that will execute route statement with number given as parameter.

- `exec` - Execute a shell command.

  The command accepts one parameter of type `char*`. The string given as parameter will be passed to `system` function which will in turn execute `/bin/sh -c <string>`.

- `revert_uri` - Revert changes made to the Request URI.

  If there is a new URI stored in new_uri of sip_msg structure, it will be freed. The original Request URI will be used when forwarding the message.

  If there is a valid URI in parsed_uri field of sip_msg structure (indicated by parsed_uri_ok field), it will be freed too.

- `set_host` - Change hostname of Request URI to value given as parameter.

  If there is a URI in new_uri field, it will be modified, otherwise the original Request URI will be modified.

- `set_hostport` - change hostname and port of Request URI to value given as string parameter.

  If there is a URI in new_uri field, it will be modified, otherwise the original Request URI will be modified.

- `set_user` - Set username part of Request URI to string given as parameter.

  If there is a URI in new_uri field, it will be modified, otherwise the original Request URI will be modified.

- `set_userpass` - Set username and password part of Request URI to string given as parameter.

  If there is a URI in new_uri field, it will be modified, otherwise the original Request URI will be modified.

- `set_port` - Set port of Request URI to value given as parameter.

  If there is a URI in new_uri field, it will be modified, otherwise the original Request URI will be modified.

- `set_uri` - Set a new Request URI.

  If there is a URI in new_uri field, it will be freed. If there is a valid URI in parsed_uri field, it will be freed too.

  Then URI given as parameter will be stored in new_uri field. (If new_uri contains a URI it will be used instead of Request URI when forwarding the message).

- `prefix` - Set the parameter as username prefix.

  The string will be put immediately after "sip:" part of the Request URI.

  If there is a URI in new_uri field, it will be modified, otherwise the original Request URI will be modified.

- `strip` - Remove first n characters of username in Request URI.

  If there is a URI in new_uri field, it will be modified, otherwise the original Request URI will be modified.

- `if` - if Statement.

  There is an expression associated with the command and one or two linked lists of commands. The expression is a regular expression compiled into binary form in the fixup when the config file was compiled.

  The expression will be evaluated now. If the result is > 0, the first linked list will be executed using `run_action` function. The linked list represents command enclosed in curly braces of `if` command.

  Otherwise, if there is the second list, it will be executed in the same way. The second list represents commands of `else` statement.

- `module` - Execute a function exported by a module.

  When a command in a route statement is not recognized by the core itself (i.e. it is not one of commands handled by the core itself), list of exported functions of all loaded modules will be searched for a function with corresponding name and number of parameters.

  If the function was found, `module` command (this one) will be created and pointer to the function will be stored in p1.data field.

  So, this command will simply call function whose pointer is in p1.data field and will pass 2 parameters to the function. If one or both of the parameters were not used, 0 will be passed instead.

  Return value of the function will be returned as return value of `module` command.

  This command makes SER pretty extensible while the core itself is still reasonably small and clean. Additional functionality is put in modules and loaded only when needed.