Postfix3.3.1
dict_sockmap.c
[詳解]
1 /*++
2 /* NAME
3 /* dict_sockmap 3
4 /* SUMMARY
5 /* dictionary manager interface to Sendmail-style socketmap server
6 /* SYNOPSIS
7 /* #include <dict_sockmap.h>
8 /*
9 /* DICT *dict_sockmap_open(map, open_flags, dict_flags)
10 /* const char *map;
11 /* int open_flags;
12 /* int dict_flags;
13 /* DESCRIPTION
14 /* dict_sockmap_open() makes a Sendmail-style socketmap server
15 /* accessible via the generic dictionary operations described
16 /* in dict_open(3). The only implemented operation is dictionary
17 /* lookup. This map type can be useful for simulating a dynamic
18 /* lookup table.
19 /*
20 /* Postfix socketmap names have the form inet:host:port:socketmap-name
21 /* or unix:pathname:socketmap-name, where socketmap-name
22 /* specifies the socketmap name that the socketmap server uses.
23 /*
24 /* To test this module, build the netstring and dict_open test
25 /* programs. Run "./netstring nc -l portnumber" as the server,
26 /* and "./dict_open socketmap:127.0.0.1:portnumber:socketmapname"
27 /* as the client.
28 /* PROTOCOL
29 /* .ad
30 /* .fi
31 /* The socketmap class implements a simple protocol: the client
32 /* sends one request, and the server sends one reply.
33 /* ENCODING
34 /* .ad
35 /* .fi
36 /* Each request and reply are sent as one netstring object.
37 /* REQUEST FORMAT
38 /* .ad
39 /* .fi
40 /* .IP "<mapname> <space> <key>"
41 /* Search the specified socketmap under the specified key.
42 /* REPLY FORMAT
43 /* .ad
44 /* .fi
45 /* Replies must be no longer than 100000 characters (not including
46 /* the netstring encapsulation), and must have the following
47 /* form:
48 /* .IP "OK <space> <data>"
49 /* The requested data was found.
50 /* .IP "NOTFOUND <space>"
51 /* The requested data was not found.
52 /* .IP "TEMP <space> <reason>"
53 /* .IP "TIMEOUT <space> <reason>"
54 /* .IP "PERM <space> <reason>"
55 /* The request failed. The reason, if non-empty, is descriptive
56 /* text.
57 /* SECURITY
58 /* This map cannot be used for security-sensitive information,
59 /* because neither the connection nor the server are authenticated.
60 /* SEE ALSO
61 /* dict(3) generic dictionary manager
62 /* netstring(3) netstring stream I/O support
63 /* DIAGNOSTICS
64 /* Fatal errors: out of memory, unknown host or service name,
65 /* attempt to update or iterate over map.
66 /* BUGS
67 /* The protocol limits are not yet configurable.
68 /* LICENSE
69 /* .ad
70 /* .fi
71 /* The Secure Mailer license must be distributed with this software.
72 /* AUTHOR(S)
73 /* Wietse Venema
74 /* IBM T.J. Watson Research
75 /* P.O. Box 704
76 /* Yorktown Heights, NY 10598, USA
77 /*--*/
78 
79  /*
80  * System library.
81  */
82 #include <sys_defs.h>
83 #include <errno.h>
84 #include <string.h>
85 #include <ctype.h>
86 
87  /*
88  * Utility library.
89  */
90 #include <mymalloc.h>
91 #include <msg.h>
92 #include <vstream.h>
93 #include <auto_clnt.h>
94 #include <netstring.h>
95 #include <split_at.h>
96 #include <stringops.h>
97 #include <htable.h>
98 #include <dict_sockmap.h>
99 
100  /*
101  * Socket map data structure.
102  */
103 typedef struct {
104  DICT dict; /* parent class */
105  char *sockmap_name; /* on-the-wire socketmap name */
106  VSTRING *rdwr_buf; /* read/write buffer */
107  HTABLE_INFO *client_info; /* shared endpoint name and handle */
108 } DICT_SOCKMAP;
109 
110  /*
111  * Default limits.
112  */
113 #define DICT_SOCKMAP_DEF_TIMEOUT 100 /* connect/read/write timeout */
114 #define DICT_SOCKMAP_DEF_MAX_REPLY 100000 /* reply size limit */
115 #define DICT_SOCKMAP_DEF_MAX_IDLE 10 /* close idle socket */
116 #define DICT_SOCKMAP_DEF_MAX_TTL 100 /* close old socket */
117 
118  /*
119  * Class variables.
120  */
121 static int dict_sockmap_timeout = DICT_SOCKMAP_DEF_TIMEOUT;
122 static int dict_sockmap_max_reply = DICT_SOCKMAP_DEF_MAX_REPLY;
123 static int dict_sockmap_max_idle = DICT_SOCKMAP_DEF_MAX_IDLE;
124 static int dict_sockmap_max_ttl = DICT_SOCKMAP_DEF_MAX_TTL;
125 
126  /*
127  * The client handle is shared between socketmap instances that have the
128  * same inet:host:port or unix:pathame information. This could be factored
129  * out as a general module for reference-counted handles of any kind.
130  */
131 static HTABLE *dict_sockmap_handles; /* shared handles */
132 
133 typedef struct {
134  AUTO_CLNT *client_handle; /* the client handle */
135  int refcount; /* the reference count */
137 
138 #define DICT_SOCKMAP_RH_NAME(ht) (ht)->key
139 #define DICT_SOCKMAP_RH_HANDLE(ht) \
140  ((DICT_SOCKMAP_REFC_HANDLE *) (ht)->value)->client_handle
141 #define DICT_SOCKMAP_RH_REFCOUNT(ht) \
142  ((DICT_SOCKMAP_REFC_HANDLE *) (ht)->value)->refcount
143 
144  /*
145  * Socketmap protocol elements.
146  */
147 #define DICT_SOCKMAP_PROT_OK "OK"
148 #define DICT_SOCKMAP_PROT_NOTFOUND "NOTFOUND"
149 #define DICT_SOCKMAP_PROT_TEMP "TEMP"
150 #define DICT_SOCKMAP_PROT_TIMEOUT "TIMEOUT"
151 #define DICT_SOCKMAP_PROT_PERM "PERM"
152 
153  /*
154  * SLMs.
155  */
156 #define STR(x) vstring_str(x)
157 #define LEN(x) VSTRING_LEN(x)
158 
159 /* dict_sockmap_lookup - socket map lookup */
160 
161 static const char *dict_sockmap_lookup(DICT *dict, const char *key)
162 {
163  const char *myname = "dict_sockmap_lookup";
164  DICT_SOCKMAP *dp = (DICT_SOCKMAP *) dict;
165  AUTO_CLNT *sockmap_clnt = DICT_SOCKMAP_RH_HANDLE(dp->client_info);
166  VSTREAM *fp;
167  int netstring_err;
168  char *reply_payload;
169  int except_count;
170  const char *error_class;
171 
172  if (msg_verbose)
173  msg_info("%s: key %s", myname, key);
174 
175  /*
176  * Optionally fold the key.
177  */
178  if (dict->flags & DICT_FLAG_FOLD_MUL) {
179  if (dict->fold_buf == 0)
180  dict->fold_buf = vstring_alloc(100);
181  vstring_strcpy(dict->fold_buf, key);
182  key = lowercase(STR(dict->fold_buf));
183  }
184 
185  /*
186  * We retry connection-level errors once, to make server restarts
187  * transparent.
188  */
189  for (except_count = 0; /* see below */ ; except_count++) {
190 
191  /*
192  * Look up the stream.
193  */
194  if ((fp = auto_clnt_access(sockmap_clnt)) == 0) {
195  msg_warn("table %s:%s lookup error: %m", dict->type, dict->name);
196  dict->error = DICT_ERR_RETRY;
197  return (0);
198  }
199 
200  /*
201  * Set up an exception handler.
202  */
203  netstring_setup(fp, dict_sockmap_timeout);
204  if ((netstring_err = vstream_setjmp(fp)) == 0) {
205 
206  /*
207  * Send the query. This may raise an exception.
208  */
209  vstring_sprintf(dp->rdwr_buf, "%s %s", dp->sockmap_name, key);
210  NETSTRING_PUT_BUF(fp, dp->rdwr_buf);
211 
212  /*
213  * Receive the response. This may raise an exception.
214  */
215  netstring_get(fp, dp->rdwr_buf, dict_sockmap_max_reply);
216 
217  /*
218  * If we got here, then no exception was raised.
219  */
220  break;
221  }
222 
223  /*
224  * Handle exceptions.
225  */
226  else {
227 
228  /*
229  * We retry a broken connection only once.
230  */
231  if (except_count == 0 && netstring_err == NETSTRING_ERR_EOF
232  && errno != ETIMEDOUT) {
233  auto_clnt_recover(sockmap_clnt);
234  continue;
235  }
236 
237  /*
238  * We do not retry other errors.
239  */
240  else {
241  msg_warn("table %s:%s lookup error: %s",
242  dict->type, dict->name,
243  netstring_strerror(netstring_err));
244  dict->error = DICT_ERR_RETRY;
245  return (0);
246  }
247  }
248  }
249 
250  /*
251  * Parse the reply.
252  */
254  reply_payload = split_at(STR(dp->rdwr_buf), ' ');
255  if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_OK) == 0) {
256  dict->error = 0;
257  return (reply_payload);
258  } else if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_NOTFOUND) == 0) {
259  dict->error = 0;
260  return (0);
261  }
262  /* We got no definitive reply. */
263  if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_TEMP) == 0) {
264  error_class = "temporary";
265  dict->error = DICT_ERR_RETRY;
266  } else if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_TIMEOUT) == 0) {
267  error_class = "timeout";
268  dict->error = DICT_ERR_RETRY;
269  } else if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_PERM) == 0) {
270  error_class = "permanent";
271  dict->error = DICT_ERR_CONFIG;
272  } else {
273  error_class = "unknown";
274  dict->error = DICT_ERR_RETRY;
275  }
276  while (reply_payload && ISSPACE(*reply_payload))
277  reply_payload++;
278  msg_warn("%s:%s socketmap server %s error%s%.200s",
279  dict->type, dict->name, error_class,
280  reply_payload && *reply_payload ? ": " : "",
281  reply_payload && *reply_payload ?
282  printable(reply_payload, '?') : "");
283  return (0);
284 }
285 
286 /* dict_sockmap_close - close socket map */
287 
288 static void dict_sockmap_close(DICT *dict)
289 {
290  const char *myname = "dict_sockmap_close";
291  DICT_SOCKMAP *dp = (DICT_SOCKMAP *) dict;
292 
293  if (dict_sockmap_handles == 0 || dict_sockmap_handles->used == 0)
294  msg_panic("%s: attempt to close a non-existent map", myname);
295  vstring_free(dp->rdwr_buf);
296  myfree(dp->sockmap_name);
297  if (--DICT_SOCKMAP_RH_REFCOUNT(dp->client_info) == 0) {
299  htable_delete(dict_sockmap_handles,
301  }
302  if (dict->fold_buf)
303  vstring_free(dict->fold_buf);
304  dict_free(dict);
305 }
306 
307 /* dict_sockmap_open - open socket map */
308 
309 DICT *dict_sockmap_open(const char *mapname, int open_flags, int dict_flags)
310 {
311  DICT_SOCKMAP *dp;
312  char *saved_name = 0;
313  char *sockmap;
314  DICT_SOCKMAP_REFC_HANDLE *ref_handle;
315  HTABLE_INFO *client_info;
316 
317  /*
318  * Let the optimizer worry about eliminating redundant code.
319  */
320 #define DICT_SOCKMAP_OPEN_RETURN(d) do { \
321  DICT *__d = (d); \
322  if (saved_name != 0) \
323  myfree(saved_name); \
324  return (__d); \
325  } while (0)
326 
327  /*
328  * Sanity checks.
329  */
330  if (open_flags != O_RDONLY)
332  open_flags, dict_flags,
333  "%s:%s map requires O_RDONLY access mode",
334  DICT_TYPE_SOCKMAP, mapname));
335  if (dict_flags & DICT_FLAG_NO_UNAUTH)
337  open_flags, dict_flags,
338  "%s:%s map is not allowed for security-sensitive data",
339  DICT_TYPE_SOCKMAP, mapname));
340 
341  /*
342  * Separate the socketmap name from the socketmap server name.
343  */
344  saved_name = mystrdup(mapname);
345  if ((sockmap = split_at_right(saved_name, ':')) == 0)
347  open_flags, dict_flags,
348  "%s requires server:socketmap argument",
350 
351  /*
352  * Use one reference-counted client handle for all socketmaps with the
353  * same inet:host:port or unix:pathname information.
354  *
355  * XXX Todo: graceful degradation after endpoint syntax error.
356  */
357  if (dict_sockmap_handles == 0)
358  dict_sockmap_handles = htable_create(1);
359  if ((client_info = htable_locate(dict_sockmap_handles, saved_name)) == 0) {
360  ref_handle = (DICT_SOCKMAP_REFC_HANDLE *) mymalloc(sizeof(*ref_handle));
361  client_info = htable_enter(dict_sockmap_handles,
362  saved_name, (void *) ref_handle);
363  /* XXX Late initialization, so we can reuse macros for consistency. */
364  DICT_SOCKMAP_RH_REFCOUNT(client_info) = 1;
365  DICT_SOCKMAP_RH_HANDLE(client_info) =
366  auto_clnt_create(saved_name, dict_sockmap_timeout,
367  dict_sockmap_max_idle, dict_sockmap_max_ttl);
368  } else
369  DICT_SOCKMAP_RH_REFCOUNT(client_info) += 1;
370 
371  /*
372  * Instantiate a socket map handle.
373  */
374  dp = (DICT_SOCKMAP *) dict_alloc(DICT_TYPE_SOCKMAP, mapname, sizeof(*dp));
375  dp->rdwr_buf = vstring_alloc(100);
376  dp->sockmap_name = mystrdup(sockmap);
377  dp->client_info = client_info;
378  dp->dict.lookup = dict_sockmap_lookup;
379  dp->dict.close = dict_sockmap_close;
380  /* Don't look up parent domains or network superblocks. */
381  dp->dict.flags = dict_flags | DICT_FLAG_PATTERN;
382 
384 }
int msg_verbose
Definition: msg.c:177
#define DICT_ERR_CONFIG
Definition: dict.h:179
VSTREAM * auto_clnt_access(AUTO_CLNT *auto_clnt)
Definition: auto_clnt.c:251
void myfree(void *ptr)
Definition: mymalloc.c:207
HTABLE_INFO * htable_locate(HTABLE *table, const char *key)
Definition: htable.c:242
char * mystrdup(const char *str)
Definition: mymalloc.c:225
#define DICT_FLAG_NO_UNAUTH
Definition: dict.h:123
void(* close)(struct DICT *)
Definition: dict.h:87
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
VSTRING * netstring_get(VSTREAM *stream, VSTRING *buf, ssize_t limit)
Definition: netstring.c:268
char * name
Definition: dict.h:80
ssize_t used
Definition: htable.h:27
int flags
Definition: dict.h:81
#define DICT_ERR_RETRY
Definition: dict.h:178
#define DICT_SOCKMAP_DEF_MAX_REPLY
Definition: dict_sockmap.c:114
#define DICT_SOCKMAP_PROT_NOTFOUND
Definition: dict_sockmap.c:148
void netstring_setup(VSTREAM *stream, int timeout)
Definition: netstring.c:182
#define DICT_TYPE_SOCKMAP
Definition: dict_sockmap.h:22
#define DICT_SOCKMAP_RH_NAME(ht)
Definition: dict_sockmap.c:138
#define vstream_setjmp(stream)
Definition: vstream.h:248
Definition: htable.h:25
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
#define VSTRING_TERMINATE(vp)
Definition: vstring.h:74
HTABLE * htable_create(ssize_t size)
Definition: htable.c:179
Definition: dict.h:78
char * type
Definition: dict.h:79
AUTO_CLNT * auto_clnt_create(const char *service, int timeout, int max_idle, int max_ttl)
Definition: auto_clnt.c:271
void msg_warn(const char *fmt,...)
Definition: msg.c:215
#define DICT_SOCKMAP_DEF_TIMEOUT
Definition: dict_sockmap.c:113
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define DICT_SOCKMAP_PROT_PERM
Definition: dict_sockmap.c:151
#define STR(x)
Definition: dict_sockmap.c:156
const char * netstring_strerror(int err)
Definition: netstring.c:371
int error
Definition: dict.h:94
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
#define DICT_SOCKMAP_DEF_MAX_TTL
Definition: dict_sockmap.c:116
char * lowercase(char *string)
Definition: lowercase.c:34
void auto_clnt_free(AUTO_CLNT *auto_clnt)
Definition: auto_clnt.c:316
const char *(* lookup)(struct DICT *, const char *)
Definition: dict.h:82
#define DICT_SOCKMAP_RH_HANDLE(ht)
Definition: dict_sockmap.c:139
#define DICT_FLAG_PATTERN
Definition: dict.h:115
#define DICT_SOCKMAP_PROT_TEMP
Definition: dict_sockmap.c:149
void dict_free(DICT *)
Definition: dict_alloc.c:163
#define DICT_SOCKMAP_PROT_TIMEOUT
Definition: dict_sockmap.c:150
#define NETSTRING_ERR_EOF
Definition: netstring.h:23
char * sockmap_name
Definition: dict_sockmap.c:105
#define DICT_SOCKMAP_RH_REFCOUNT(ht)
Definition: dict_sockmap.c:141
void auto_clnt_recover(AUTO_CLNT *auto_clnt)
Definition: auto_clnt.c:239
#define DICT_SOCKMAP_PROT_OK
Definition: dict_sockmap.c:147
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
char * split_at(char *string, int delimiter)
Definition: split_at.c:53
#define DICT_FLAG_FOLD_MUL
Definition: dict.h:125
#define DICT_SOCKMAP_DEF_MAX_IDLE
Definition: dict_sockmap.c:115
#define ISSPACE(c)
Definition: sys_defs.h:1753
#define DICT_SOCKMAP_OPEN_RETURN(d)
DICT * dict_alloc(const char *, const char *, ssize_t)
Definition: dict_alloc.c:135
VSTRING * rdwr_buf
Definition: dict_sockmap.c:106
VSTRING * fold_buf
Definition: dict.h:92
char * printable(char *string, int replacement)
Definition: printable.c:49
void htable_delete(HTABLE *table, const char *key, void(*free_fn)(void *))
Definition: htable.c:257
char * split_at_right(char *string, int delimiter)
Definition: split_at.c:64
DICT * dict_sockmap_open(const char *mapname, int open_flags, int dict_flags)
Definition: dict_sockmap.c:309
HTABLE_INFO * client_info
Definition: dict_sockmap.c:107
#define NETSTRING_PUT_BUF(str, buf)
Definition: netstring.h:41
DICT * dict_surrogate(const char *dict_type, const char *dict_name, int open_flags, int dict_flags, const char *fmt,...)
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
HTABLE_INFO * htable_enter(HTABLE *table, const char *key, void *value)
Definition: htable.c:212
void msg_info(const char *fmt,...)
Definition: msg.c:199