![]() |
SMAP |
A Socket Map Server |
Sergey Poznyakoff |
Smap Manual (split by chapter): | ![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
? |
Smap is shipped with a set of loadable modules, which are installed in its default module directory, ‘$prefix/lib/smap’. The modules are configurable on a per-module (see section Loadable Modules), and per-database (see section Databases) levels.
Smap version 1.1 contains the following modules: ‘echo’, ‘mailutils’ and ‘guile’. These are described in detail in the following sections.
The echo module is the simplest of all modules. It sends back a static reply string, no matter what the query was. This module is useful for default databases, which catch erroneous or not handled queries.
The module needs no additional arguments for initialization. Normal loading statement is:
module echo echo |
Database initialization function treats its arguments as a string to be sent in reply to all queries. An example database definition:
database default echo NOTFOUND [no such map] |
Such a definition is normally used as a target of the ‘default’ dispatch rule:
dispatch default database default |
This module uses GNU Mailutils (http://www.gnu.org/software/mailutils) and provides two main modes:
This mode uses GNU Mailutils authorization mechanism to obtain
user data (similar to the system ‘getpwnam’ routine) and returns
positive reply if the data were retrieved and negative reply
otherwise. See section Example: Using smapd
with MeTA1, for an example on how to use it as a local
user and alias database.
This mode allows to check whether the user's mailbox exceeded the allotted quota, and if not, whether it is able to accept a message of the given size without exceeding it. The mode name is an abbreviation of Mailbox Quota.
In the discussion below we often refer to meta-variable expansion in strings. This is a process, whereby any sequence ‘${variable}’ is replaced with the value of variable. The defined variables are:
If the key was not found or some error occurred, this variable expands to a short diagnostics string, suitable for return message. Otherwise, expands to empty string.
The ‘name’ field from the retrieved record. Empty string if the user not found.
The ‘passwd’ field from the retrieved record. Empty string if the user not found.
The ‘uid’ field from the retrieved record. If the user was not found, expands to ‘-1’.
The ‘gid’ field from the retrieved record. If the user was not found, expands to ‘-1’.
The ‘gecos’ field from the retrieved record. Empty string if the user not found.
The ‘dir’ field from the retrieved record. Empty string if the user not found.
The ‘shell’ field from the retrieved record. Empty string if the user not found.
The ‘mailbox’ field from the retrieved record. Empty string if the user not found.
The ‘quota’ field from the retrieved record. If the user was not found, expands to ‘NONE’.
Expected message size, in bytes. Defined only in ‘mbq’ mode.
module mailutils mailutils [args] |
Arguments are:
Verbosely trace the processing of the main Mailutils configuration files.
Dump the parse tree from the Mailutils configuration.
Declare default positive reply string. This string is returned when the underlying database was able to found the requested key. Prior to returning, str is subject to meta-variable expansion, as described above.
Default positive reply string is ‘OK’.
Declare default negative reply string. This string is returned when the underlying database failed to found the requested key. Prior to returning, str is subject to meta-variable expansion.
Default negative reply string is ‘NOTFOUND’.
Declare a reply to be returned on error. Prior to returning, str is subject to meta-variable expansion. Default string is ‘NOTFOUND’.
The module reads most of its configuration settings from the main
Mailutils configuration file. See Mailutils Configuration File: (mailutils)configuration section `configuration' in GNU Mailutils Manual, for a description of GNU
Mailutils configuration system. It looks for smap
-specific
settings in the section ‘program smap-mailutils’.
Statement | Reference |
---|---|
server | See Mailutils Configuration File: (mailutils)Server Settings section `Server Settings' in GNU Mailutils Manual. |
auth | See Mailutils Configuration File: (mailutils)Auth Statement section `Auth Statement' in GNU Mailutils Manual. |
pam | See Mailutils Configuration File: (mailutils)PAM Statement section `PAM Statement' in GNU Mailutils Manual. |
virtdomain | See Mailutils Configuration File: (mailutils)Virtdomain Statement section `Virtdomain Statement' in GNU Mailutils Manual. |
radius | See Mailutils Configuration File: (mailutils)Radius Statement section `Radius Statement' in GNU Mailutils Manual. |
sql | See Mailutils Configuration File: (mailutils)SQL Statement section `SQL Statement' in GNU Mailutils Manual. |
ldap | See Mailutils Configuration File: (mailutils)LDAP Statement section `LDAP Statement' in GNU Mailutils Manual. |
debug | See Mailutils Configuration File: (mailutils)Debug Statement section `Debug Statement' in GNU Mailutils Manual. |
logging | See Mailutils Configuration File: (mailutils)Logging Statement section `Logging Statement' in GNU Mailutils Manual. |
include | See Mailutils Configuration File: (mailutils)Include section `Include' in GNU Mailutils Manual. |
The module uses GNU Mailutils authorization databases to obtain the requested data. This concept is described in detail in Mailutils Configuration File: (mailutils)Auth Statement section `Auth Statement' in GNU Mailutils Manual.
Mailutils databases are normally declared as follows:
database name mailutils mode=mode [args] |
Here, name is the database name, mode is ‘auth’ if the database should work in auth mode, and ‘mbq’ if it should run in mbq mode. If the ‘mode’ argument is omitted, ‘auth’ is assumed. Optional args may be used to supply additional database configuration. These are:
Declare positive reply string. This string is returned when the underlying database was able to found the requested key. Prior to returning, str is subject to meta-variable expansion, as described above.
Default positive reply string is ‘OK’, unless overridden by the module-level ‘positive-reply’ option (see section positive-reply.
Declare negative reply string. This string is returned when the underlying database failed to found the requested key. Prior to returning, str is subject to meta-variable expansion.
Default negative reply string is ‘NOTFOUND’, unless overridden by the module-level ‘positive-reply’ option (see section negative-reply.
Declare a reply to be returned on error. Prior to returning, str is subject to meta-variable expansion. Default string is ‘NOTFOUND’, unless overridden by the module-level ‘positive-reply’ option (see section onerr-reply.
Mailutils module in ‘auth’ mode uses GNU Mailutils authorization mechanism to obtain user data. It returns ‘positive-reply’ if the data were retrieved and ‘negative-reply’ otherwise. This mode is often used for databases of local users and aliases. The key is normally a user name (either local part or fully qualified).
See section Example: Using smapd
with MeTA1, for an example on how to use it.
MBQ, or Mailbox Quota mode, uses key as the name of a local user. It obtains the user parameters via Mailutils authorization mechanism and then switches to this user privileges and opens his mailbox for a brief period of time. After opening it determines the mailbox size and closes it. The mode returns ‘positive-reply’ if the mailbox size is less than the quota, and ‘netagive-reply’ otherwise.
If the key value consists of two words, separated by whitespace, then the first word is used as a user name, and the second one as a size of a message which is about to be delivered to that user's mailbox (the size may be optionally prefixed by ‘SIZE=’). In this case, ‘positive-reply’ is returned if the actual mailbox size plus the message size is less than quota.
Two additional meta-variables may be used in reply templates to return quota-related information:
Expected message size, in bytes. Defined only in ‘mbq’ mode.
The following example shows a definition of ‘mbq’ database which the author uses on his servers:
database mbq mailutils mode=mbq \ positive-reply="OK [${diag}] ${mailbox} ${mbsize} ${quota}"\ negative-reply="NOTFOUND [${diag}] ${mailbox} ${mbsize} ${quota}"\ onerror-reply="NOTFOUND [${diag}]" |
The ‘diag’ meta-variable contains a diagnostic string suitable for passing it back to the MTA. For example, in the case of ‘negative-reply’, ‘${diag}’ expands to:
mailbox quota exceeded for this recipient |
if the mailbox has grown beyond the allowed quota, and
message would exceed maximum mailbox size for this recipient |
if message of the given size cannot be delivered to mailbox without violating its quota.
Notice, that this mode requires superuser privileges.
Guile is an acronym for GNU's Ubiquitous Intelligent Language for Extensions. It provides a Scheme interpreter conforming to the R5RS language specification and a number of convenience functions. For information about the language, refer to (r5rs)Top section `Top' in Revised(5) Report on the Algorithmic Language Scheme. For a detailed description of Guile and its features, see (guile)Top section `Overview' in The Guile Reference Manual.
The guile
module provides an interface to Guile which
allows for writing Smap modules in Scheme. The module is loaded
using the following configuration file statement:
module name guile [args] |
Optional args are:
Enable Guile debugging and stack traces.
Disable Guile debugging and stack traces (default).
Append directories from path to the list of directories which should be searched for Scheme modules and libraries. The path must be a list of directory names, separated by colons.
This option modifies the value of Guile's %load-path
variable.
See the section Configuration and Installation in the Guile Reference Manual.
Specifies the name of a Scheme source file that must be loaded in order to initialize the module. The file is looked up using ‘%load-path’ variable.
The init-args
parameter supplies additional arguments to the
module. They will be accessible to the ‘script’ via
the command-line
function.
This parameter specifies the name of a function that will be invoked to perform the initialization of the module and of particular databases. Default name is ‘init’. See section Guile Initialization, for a description of initialization sequence.
Guile databases are declared using the following syntax:
database dbname modname [args] [cmdline] |
where: dbname gives the name for this database and modname
is the name given to Guile module in module
statement (see
above).
Optional args override global settings given in the
module
statement. The following options are understood:
init-script
, init-args
, and init-fun
. Their
meaning is the same as for module
statement (see above),
except that they affect only this particular database.
Any additional arguments, referenced as cmdline above, are
be passed to the Guile open-db
callback function (see open-db).
Any database handled by guile
module is associated with a
virtual function table. This table is an association list which
supplies to the module the Scheme call-back functions implemented to
perform particular tasks on that database. In this list,
the car
of each element contains the name of a function, and
its cdr
gives the corresponding function. The defined function
names and their semantics are described in the following table:
Initialize the module.
Close the module, releasing any resources held by it.
Open the database.
Close the database.
Handle a socket map query
Handle a transformation request (see section Transformations).
For example, the following is a valid virtual function table:
(list (cons "open" open-module) (cons "close" close-module) (cons "query" run-query)) |
Apart from per-database virtual tables, there is also a global virtual function table, which is used to supply the entries missing in the former. Both tables are created during the module initialization, as described in the next subsection.
Particular virtual functions are described in Guile API.
Guile modules are executed in a specially prepared environment.
Current error port is redirected so that everything written to it ends
up in the smapd
error stream. So, if smapd
is
writing its log to syslog, everything you write to
‘(current-error-port)’ will be written to syslog as well. The
port is line-buffered. For example, the following code:
(with-output-to-port (current-error-port) (lambda () (display "The diagnostics follows:") (newline) (display "Module opened") (newline))) |
will result in two lines in your syslog file, which will look like
Jun 19 12:49:05 netbox smapd[7503]: The diagnostics follows Jun 19 12:49:05 netbox smapd[7503]: Module opened |
For any debugging output, use smap-debug-port
. This port is
configured so that everything written to it is explicitly marked as
being debug output. If smapd
logs to stderr, it will be
prefixed with ‘DEBUG:’, and if it logs to syslog, the output will
be logged with ‘LOG_DEBUG’ priority.
Finally, current output port is closed for any functions, excepting ‘query’ (see query-db). For ‘query’ function, it is redirected so that anything written to it is reformatted according to the socket map protocol (see section The Sockmap Protocol) and sent back as a reply to the client.
The module
configuration statement causes loading and
initialization of the guile
module:
module modname guile [init-script=‘script’] \ [init-fun=function"] |
Upon module initialization stage, the module attempts to load the
file named ‘script’. The file is loaded using
primitive-load-path
call (see primitive-load-path: (guile)Loading section `Loading' in The Guile Reference Manual), i.e. it is searched in the Guile
load path. The init-fun
parameter supplies the name of the
initialization function. This Scheme function returns virtual
function tables for the module itself and for each database that uses
this module. It must be declared as follows:
(define (function arg) …) |
This function is called several times. First of all, it is called after
script is loaded. This time it is given #f
as its
argument, and its return value is saved as a global function table.
Then, it is called for each database
statement that uses module
modname (defined in the module
statement above), e.g.:
database dbname modname … |
This time, it is given dbname as its argument and its return is stored as the virtual function table for this particular database.
The following example function returns a complete virtual function table:
(define (my-smap-init arg) (list (cons "init" db-init) (cons "done" db-done) (cons "open" db-open) (cons "close" db-close) (cons "query" db-query) (cons "xform" db-xform))) |
This subsection describes callback functions that a Guile database module must provide. The description of each function begins with the function prototype and its entry in the virtual function table.
Virtual table: (cons "open" open-db)
Open the database. The argument name contains database name as
given in dbname
of the database
declaration
(see section Databases). Optional argument args is a list of
command line parameters obtained from cmdline in database
statement (see guile-cmdline). For example, if the configuration
file contained:
database foo guile db=file 1 no |
then the open-db
callback will be called as:
(open-db "foo" '("db=file" "1" "no")) |
The open-db
callback returns a database handle, i.e. an
opaque Scheme object which identifies this database, and keeps its
internal state. This value, hereinafter named dbh,
will be passed to another callback functions that need to access the
database.
The unspecified return value indicates an error.
Virtual Table: (cons "close" close-db)
Close the database. This function is called during the cleanup
procedure, before termination of smapd
child process. The
argument dbh
is a database handle returned by open-db
.
The return value from close-db
is ignored. To communicate
errors to the daemon, throw an exception.
Virtual Table: (cons "close" close-db)
Perform the query. Arguments are:
A database handle returned by open-db
.
The map name.
The lookup key
If this query came over a UNIX socket, this argument is ‘()’. Otherwise, if the query came over an INET socket, rest is a list of two network socket addresses (see (guile)Network Socket Address section `Network Socket Address' in The Guile Reference Manual): first element is the address of the remote party (client), second element is the address of the server that is handling the query.
This function must write the reply, terminated with a newline, to the current output port, e.g.:
(define-public (smap-query handle map arg . rest) (display "NOTFOUND") (newline)) |
Virtual Table: (cons "xform" xform-db)
Transform the argument arg. Arguments dbh and
rest have the same meaning as in query-db.
Returns transformed value or ‘#f’ if no transformation applies. This callback may be used to alter map or key values using ‘guile’ module (see section Transformations). The following example function removes optional domain part from its argument:
(define (smap-xform handle arg . rest) (let ((arg-parts (string-split arg #\@))) (if (null? (cdr arg-parts)) #f (car arg-parts)))) |
The following snippet from the ‘smapd.conf’ file shows how to apply it:
database localpart guile init-script=local.scm dispatch key like *@* transform key localpart |
The mysql
module provides interface to MySQL database
management system. It may be used to build smap databases over SQL
ones.
The SQL database to use may be configured either globally, when loading the module, or locally, when defining a smap database. If a database definition lacks SQL configuration statements, then it attempts to use a globally defined connection.
Each database is configured with a SQL query template, and a set of smap reply templates to use. When dispatched a sockmap query, the database expands the SQL query template using the actual values of ‘${map}’ (the map name) and ‘${key}’ (the key value) and sends the expanded query to the | MySQL server. If the server responds with a non-empty set of tuples, the positive reply template is expanded and the result is used as a response. Otherwise, if the query produced an empty set, the smap database uses the negative reply template to create the response.
The SQL database is configured using the following options:
Set the name of the MySQL configuration file to read. By default ‘/etc/my.cnf’ is used.
Set the name of the group in the MySQL configuration file, from where to read the configuration options.
The statements above allow to keep all security-sensitive information, such as MySQL username and password, in an external configuration file and thus to relax permission requirements for ‘smapd.conf’. For a detailed description of the format of such external configuration file (or option file in ‘MySQL’ parlance), see MySQL option files.
In case the use of option files is not feasible for some reason,
you may specify MySQL connection and database parameters in
‘smapd.conf’ when loading the mysql
module or defining
a smap database. The following options are used to define MySQL
connection parameters:
Sets the hostname or IP address of the host running the MySQL server.
Sets port number the MySQL server is listening on. Default is 3306.
Sets the socket name, if the server is listening on a UNIX socket.
Sets the pathname to the certificate authority file, if you wish to use a secure connection to the server via SSL.
Notice, that either ‘host’ and, optionally, ‘port’ or ‘socket’ must be used. Specifying both is senseless.
MySQL database and user credentials are defined using the following options:
Sets the name of the MySQL database to use.
Sets MySQL user name.
Sets the password for accessing the MySQL database.
When using these options, it is reasonable to tighten the permissions on ‘smapd.conf’ so that no third person could see the MySQL password. The recommended permissions are ‘0600’.
MySQL query is defined using the following option:
Define MySQL query template.
The template may reference the following variables:
Table 6.1: MySQL query template variables
For example:
database alias mysql \ query="SELECT alias FROM aliases WHERE email='$key'" |
If the database definition lacks the query
option,
it will attempt to use one from the module statement. If the module
statement lacked it as well, an error is reported.
Reply templates define the responses to be given. They are given by the following options:
Defines a reply to be sent if the query returned a non-empty set of tuples. In addition to the variables described above (see mysql-query-vars), the template may also refer to the MySQL result columns, by using their names from the ‘SELECT’ part of the query. For example:
database alias mysql \ query="SELECT alias FROM aliases WHERE email='$key'" \ positive-reply="OK $alias" |
The default positive-reply
is ‘OK’.
Defines a reply to be sent if the query returned an empty set of tuples. The template may refer to the variables described in mysql-query-vars.
Default value is ‘NOTFOUND’.
Defines a reply to be sent if an error occurred when executing the query. The template may refer to the variables described in mysql-query-vars.
Default value is ‘NOTFOUND’.
The postgres
module provides interface to PostgreSQL database
management system. It may be used to build smap databases over SQL
ones.
The module is in many regards similar to mysql
module,
described above. In particular, its overall functionality is exactly
the same as described in Mysql, except, of course, that it uses
PostgreSQL databases.
A Postgres database is configured using a set of options understood
by the Postgres PQconnectdb
function. See
http://www.postgresql.org/docs/8.4/static/libpq-connect.html,
for a detailed description. The following is a short summary of the
most useful options:
Name of host to connect to. If this begins with a slash, it specifies Unix-domain communication rather than TCP/IP communication; the value is the name of the directory in which the socket file is stored.
Numeric IP address of host to connect to.
Port number to connect to at the server host, or socket file name extension for Unix-domain connections.
The database name.
PostgreSQL user name to connect as. Defaults to be the same as the
operating system name of the user running the smapd
.
Password to be used if the server demands password authentication.
Maximum wait for connection, in seconds. Zero or not specified means wait indefinitely.
Any additional command-line options to send to the server at run-time. For example, setting this to ‘-c geqo=off’ sets the session's value of the ‘geqo’ parameter to ‘off’. For a detailed discussion of the available options, see Postgres documentation(7).
This option determines whether or with what priority an SSL TCP/IP connection will be negotiated with the server. There are six modes: ‘disable’, ‘allow’, ‘prefer’, ‘require’, ‘verify-ca’ and ‘verify-full’(8).
This parameter specifies the file name of the client SSL certificate.
This parameter specifies the location for the secret key used for the client certificate.
This parameter specifies the file name of the root SSL certificate.
This parameter specifies the file name of the SSL certificate revocation list (CRL).
Kerberos service name to use when authenticating with Kerberos 5 or GSSAPI.
Service name to use for additional parameters.
Postgres SQL query and the smap replies are configured the same way
as for mysql
module (see section MySQL Query and SMAP Replies).
The following is a short summary:
Define the Postgres query template. The template may reference the following variables:
Table 6.2: Postgres query template variables
If the database definition lacks the query
option,
it will attempt to use one from the module statement. If the module
statement lacked it as well, an error is reported.
Defines a reply to be sent if the query returned a non-empty set of tuples. In addition to the variables described above (see postgres-query-vars), the template may also refer to the column names from the SQL result set.
The default positive-reply
is ‘OK’.
Defines a reply to be sent if the query returned an empty set of tuples. The template may refer to the variables described in postgres-query-vars.
Default value is ‘NOTFOUND’.
Defines a reply to be sent if an error occurred when executing the query. The template may refer to the variables described in postgres-query-vars.
Default value is ‘NOTFOUND’.
The ‘sed’ module applies sed-like s-expressions to strings to modify them. It is designed mainly for use in transformation rules (see section Transformations).
module sed sed [args] |
The following arguments may be given in the statement above:
Use case-insensitive expressions.
Use case-sensitive expressions. This is the default.
Use extended regular expressions. This is the default.
Use basic regular expressions.
Although sed
module is designed for transformations in the
first place, it may also be used as a conventional lookup database
(see section Using Sed for Lookups). The following options modify its behavior in
this mode:
Use str for positive replies.
Use str for negative replies.
Reply with str if an error occurred.
The definition of a sed databases requires a single argument: the s-expression to be applied. For example:
database dequote sed 's/<(.*)>/\1/g' |
Be sure to properly quote the expression, especially if it contains backreferences. It is preferable to use single quotes, to avoid duplicating each backslash in the expression, as shown in the example below. If the expression itself contains single quote, you may either use double-quotes to quote the entire expression:
database foo sed "s/'utf8'(.*)/u8_\\1/" |
or use escaped single quotes outside of quoted expression (a technique familiar for shell programmers):
database foo sed 's/'\''utf8'\''(.*)/u8_\1/' |
All options valid for module initialization (see section Loading sed module) may also be used in database declarations. When used so, they take precedence over module initialization options. For example, the following database definition uses basic case-insensitive regular expressions:
database bar sed noextended noicase 's/test(\([^)]\))/\1/g' |
The transformation expression is a sed
-like
replace expression of the form:
s/regexp/replace/[flags] |
where regexp is a regular expression, replace is a replacement for each part of the input that matches regexp. Both regexp and replace are described in detail in The "s" Command: (sed)The "s" Command section `The `s' Command' in GNU sed.
As in sed
, you can give several replace expressions,
separated by a semicolon.
Supported flags are:
Apply the replacement to all matches to the regexp, not just the first.
Use case-insensitive matching
regexp is an extended regular expression (see Extended regular expressions: (sed)Extended regexps section `Extended regular expressions' in GNU sed).
Only replace the numberth match of the regexp.
Note: the POSIX standard does not specify what should happen
when you mix the ‘g’ and number modifiers. The
sed
module follows the GNU sed
implementation in
this regard, so the interaction is defined to be: ignore matches
before the numberth, and then match and replace all matches from
the numberth on.
Any delimiter can be used in lieue of ‘/’, the only requirement being that it be used consistently throughout the expression. For example, the following two expressions are equivalent:
s/one/two/ s,one,two, |
Changing delimiters is often useful when the regex contains
slashes. For instance, it is more convenient to write s,/,-,
than
s/\//-/
.
The sed
module is designed primarily for argument
transformation. Nevertheless, it may also be used to define simple
look-up databases. When used in a database
clause of a
dispatch rule, the module behaves as follows. The s-expression is
applied to the key. If the result differs from the input key, the
‘positive-reply’ is returned. It the result is the same as the
input key, ‘negative-reply’ is returned. If some error occurred,
‘onerror-reply’ is returned. The reply strings may be
supplied as arguments to the database definition or to the module
loading statement. The following variables are expanded within these
strings:
The map name.
The key value.
Transformed key value. This variable is not defined for ‘onerror-reply’.
Default replies are:
Reply | Value |
---|---|
positive-reply | ‘OK ${xform}’ |
negative-reply | ‘NOTFOUND’ |
onerror-reply | ‘NOTFOUND’ |
For PostgreSQL version 8.4, see Chapter 18 in PostgreSQL Manual.
For PostgreSQL version 8.4, see Section 30.17 in PostgreSQL Manual.
![]() |
![]() |
![]() |
![]() |
![]() |
? |
Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.