Postfix3.3.1
dict_tcp.c
[詳解]
1 /*++
2 /* NAME
3 /* dict_tcp 3
4 /* SUMMARY
5 /* dictionary manager interface to tcp-based lookup tables
6 /* SYNOPSIS
7 /* #include <dict_tcp.h>
8 /*
9 /* DICT *dict_tcp_open(map, open_flags, dict_flags)
10 /* const char *map;
11 /* int open_flags;
12 /* int dict_flags;
13 /* DESCRIPTION
14 /* dict_tcp_open() makes a TCP server accessible via the generic
15 /* dictionary operations described in dict_open(3).
16 /* The only implemented operation is dictionary lookup. This map
17 /* type can be useful for simulating a dynamic lookup table.
18 /*
19 /* Map names have the form host:port.
20 /*
21 /* The TCP map class implements a very simple protocol: the client
22 /* sends a request, and the server sends one reply. Requests and
23 /* replies are sent as one line of ASCII text, terminated by the
24 /* ASCII newline character. Request and reply parameters (see below)
25 /* are separated by whitespace.
26 /* ENCODING
27 /* .ad
28 /* .fi
29 /* In request and reply parameters, the character % and any non-printing
30 /* and whitespace characters must be replaced by %XX, XX being the
31 /* corresponding ASCII hexadecimal character value. The hexadecimal codes
32 /* can be specified in any case (upper, lower, mixed).
33 /* REQUEST FORMAT
34 /* .ad
35 /* .fi
36 /* Requests are strings that serve as lookup key in the simulated
37 /* table.
38 /* .IP "get SPACE key NEWLINE"
39 /* Look up data under the specified key.
40 /* .IP "put SPACE key SPACE value NEWLINE"
41 /* This request is currently not implemented.
42 /* REPLY FORMAT
43 /* .ad
44 /* .fi
45 /* Replies must be no longer than 4096 characters including the
46 /* newline terminator, and must have the following form:
47 /* .IP "500 SPACE text NEWLINE"
48 /* In case of a lookup request, the requested data does not exist.
49 /* In case of an update request, the request was rejected.
50 /* The text gives the nature of the problem.
51 /* .IP "400 SPACE text NEWLINE"
52 /* This indicates an error condition. The text gives the nature of
53 /* the problem. The client should retry the request later.
54 /* .IP "200 SPACE text NEWLINE"
55 /* The request was successful. In the case of a lookup request,
56 /* the text contains an encoded version of the requested data.
57 /* SECURITY
58 /* This map must not 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 /* hex_quote(3) http-style quoting
63 /* DIAGNOSTICS
64 /* Fatal errors: out of memory, unknown host or service name,
65 /* attempt to update or iterate over map.
66 /* BUGS
67 /* Only the lookup method is currently implemented.
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 /* System library. */
80 
81 #include "sys_defs.h"
82 #include <unistd.h>
83 #include <string.h>
84 #include <errno.h>
85 #include <ctype.h>
86 
87 /* Utility library. */
88 
89 #include <msg.h>
90 #include <mymalloc.h>
91 #include <vstring.h>
92 #include <vstream.h>
93 #include <vstring_vstream.h>
94 #include <connect.h>
95 #include <hex_quote.h>
96 #include <dict.h>
97 #include <stringops.h>
98 #include <dict_tcp.h>
99 
100 /* Application-specific. */
101 
102 typedef struct {
103  DICT dict; /* generic members */
104  VSTRING *raw_buf; /* raw I/O buffer */
105  VSTRING *hex_buf; /* quoted I/O buffer */
106  VSTREAM *fp; /* I/O stream */
107 } DICT_TCP;
108 
109 #define DICT_TCP_MAXTRY 10 /* attempts before giving up */
110 #define DICT_TCP_TMOUT 100 /* connect/read/write timeout */
111 #define DICT_TCP_MAXLEN 4096 /* server reply size limit */
112 
113 #define STR(x) vstring_str(x)
114 
115 /* dict_tcp_connect - connect to TCP server */
116 
117 static int dict_tcp_connect(DICT_TCP *dict_tcp)
118 {
119  int fd;
120 
121  /*
122  * Connect to the server. Enforce a time limit on all operations so that
123  * we do not get stuck.
124  */
125  if ((fd = inet_connect(dict_tcp->dict.name, NON_BLOCKING, DICT_TCP_TMOUT)) < 0) {
126  msg_warn("connect to TCP map %s: %m", dict_tcp->dict.name);
127  return (-1);
128  }
129  dict_tcp->fp = vstream_fdopen(fd, O_RDWR);
130  vstream_control(dict_tcp->fp,
133 
134  /*
135  * Allocate per-map I/O buffers on the fly.
136  */
137  if (dict_tcp->raw_buf == 0) {
138  dict_tcp->raw_buf = vstring_alloc(10);
139  dict_tcp->hex_buf = vstring_alloc(10);
140  }
141  return (0);
142 }
143 
144 /* dict_tcp_disconnect - disconnect from TCP server */
145 
146 static void dict_tcp_disconnect(DICT_TCP *dict_tcp)
147 {
148  (void) vstream_fclose(dict_tcp->fp);
149  dict_tcp->fp = 0;
150 }
151 
152 /* dict_tcp_lookup - request TCP server */
153 
154 static const char *dict_tcp_lookup(DICT *dict, const char *key)
155 {
156  DICT_TCP *dict_tcp = (DICT_TCP *) dict;
157  const char *myname = "dict_tcp_lookup";
158  int tries;
159  char *start;
160  int last_ch;
161 
162 #define RETURN(errval, result) { dict->error = errval; return (result); }
163 
164  if (msg_verbose)
165  msg_info("%s: key %s", myname, key);
166 
167  /*
168  * Optionally fold the key.
169  */
170  if (dict->flags & DICT_FLAG_FOLD_MUL) {
171  if (dict->fold_buf == 0)
172  dict->fold_buf = vstring_alloc(10);
173  vstring_strcpy(dict->fold_buf, key);
174  key = lowercase(vstring_str(dict->fold_buf));
175  }
176  for (tries = 0; /* see below */ ; /* see below */ ) {
177 
178  /*
179  * Connect to the server, or use an existing connection.
180  */
181  if (dict_tcp->fp != 0 || dict_tcp_connect(dict_tcp) == 0) {
182 
183  /*
184  * Send request and receive response. Both are %XX quoted and
185  * both are terminated by newline. This encoding is convenient
186  * for data that is mostly text.
187  */
188  hex_quote(dict_tcp->hex_buf, key);
189  vstream_fprintf(dict_tcp->fp, "get %s\n", STR(dict_tcp->hex_buf));
190  if (msg_verbose)
191  msg_info("%s: send: get %s", myname, STR(dict_tcp->hex_buf));
192  last_ch = vstring_get_nonl_bound(dict_tcp->hex_buf, dict_tcp->fp,
194  if (last_ch == '\n')
195  break;
196 
197  /*
198  * Disconnect from the server if it can't talk to us.
199  */
200  if (last_ch < 0)
201  msg_warn("read TCP map reply from %s: unexpected EOF (%m)",
202  dict_tcp->dict.name);
203  else
204  msg_warn("read TCP map reply from %s: text longer than %d",
205  dict_tcp->dict.name, DICT_TCP_MAXLEN);
206  dict_tcp_disconnect(dict_tcp);
207  }
208 
209  /*
210  * Try to connect a limited number of times before giving up.
211  */
212  if (++tries >= DICT_TCP_MAXTRY)
214 
215  /*
216  * Sleep between attempts, instead of hammering the server.
217  */
218  sleep(1);
219  }
220  if (msg_verbose)
221  msg_info("%s: recv: %s", myname, STR(dict_tcp->hex_buf));
222 
223  /*
224  * Check the general reply syntax. If the reply is malformed, disconnect
225  * and try again later.
226  */
227  if (start = STR(dict_tcp->hex_buf),
228  !ISDIGIT(start[0]) || !ISDIGIT(start[1])
229  || !ISDIGIT(start[2]) || !ISSPACE(start[3])
230  || !hex_unquote(dict_tcp->raw_buf, start + 4)) {
231  msg_warn("read TCP map reply from %s: malformed reply: %.100s",
232  dict_tcp->dict.name, printable(STR(dict_tcp->hex_buf), '_'));
233  dict_tcp_disconnect(dict_tcp);
235  }
236 
237  /*
238  * Examine the reply status code. If the reply is malformed, disconnect
239  * and try again later.
240  */
241  switch (start[0]) {
242  default:
243  msg_warn("read TCP map reply from %s: bad status code: %.100s",
244  dict_tcp->dict.name, printable(STR(dict_tcp->hex_buf), '_'));
245  dict_tcp_disconnect(dict_tcp);
247  case '4':
248  if (msg_verbose)
249  msg_info("%s: soft error: %s",
250  myname, printable(STR(dict_tcp->hex_buf), '_'));
251  dict_tcp_disconnect(dict_tcp);
253  case '5':
254  if (msg_verbose)
255  msg_info("%s: not found: %s",
256  myname, printable(STR(dict_tcp->hex_buf), '_'));
257  RETURN(DICT_ERR_NONE, 0);
258  case '2':
259  if (msg_verbose)
260  msg_info("%s: found: %s",
261  myname, printable(STR(dict_tcp->raw_buf), '_'));
262  RETURN(DICT_ERR_NONE, STR(dict_tcp->raw_buf));
263  }
264 }
265 
266 /* dict_tcp_close - close TCP map */
267 
268 static void dict_tcp_close(DICT *dict)
269 {
270  DICT_TCP *dict_tcp = (DICT_TCP *) dict;
271 
272  if (dict_tcp->fp)
273  (void) vstream_fclose(dict_tcp->fp);
274  if (dict_tcp->raw_buf)
275  vstring_free(dict_tcp->raw_buf);
276  if (dict_tcp->hex_buf)
277  vstring_free(dict_tcp->hex_buf);
278  if (dict->fold_buf)
279  vstring_free(dict->fold_buf);
280  dict_free(dict);
281 }
282 
283 /* dict_tcp_open - open TCP map */
284 
285 DICT *dict_tcp_open(const char *map, int open_flags, int dict_flags)
286 {
287  DICT_TCP *dict_tcp;
288 
289  /*
290  * Sanity checks.
291  */
292  if (dict_flags & DICT_FLAG_NO_UNAUTH)
293  return (dict_surrogate(DICT_TYPE_TCP, map, open_flags, dict_flags,
294  "%s:%s map is not allowed for security sensitive data",
295  DICT_TYPE_TCP, map));
296  if (open_flags != O_RDONLY)
297  return (dict_surrogate(DICT_TYPE_TCP, map, open_flags, dict_flags,
298  "%s:%s map requires O_RDONLY access mode",
299  DICT_TYPE_TCP, map));
300 
301  /*
302  * Create the dictionary handle. Do not open the connection until the
303  * first request is made.
304  */
305  dict_tcp = (DICT_TCP *) dict_alloc(DICT_TYPE_TCP, map, sizeof(*dict_tcp));
306  dict_tcp->fp = 0;
307  dict_tcp->raw_buf = dict_tcp->hex_buf = 0;
308  dict_tcp->dict.lookup = dict_tcp_lookup;
309  dict_tcp->dict.close = dict_tcp_close;
310  dict_tcp->dict.flags = dict_flags | DICT_FLAG_PATTERN;
311  if (dict_flags & DICT_FLAG_FOLD_MUL)
312  dict_tcp->dict.fold_buf = vstring_alloc(10);
313 
314  return (DICT_DEBUG (&dict_tcp->dict));
315 }
int msg_verbose
Definition: msg.c:177
#define STR(x)
Definition: dict_tcp.c:113
#define CA_VSTREAM_CTL_TIMEOUT(v)
Definition: vstream.h:163
VSTRING * hex_buf
Definition: dict_tcp.c:105
VSTREAM * fp
Definition: dict_tcp.c:106
#define DICT_FLAG_NO_UNAUTH
Definition: dict.h:123
void(* close)(struct DICT *)
Definition: dict.h:87
#define vstring_str(vp)
Definition: vstring.h:71
#define DICT_TYPE_TCP
Definition: dict_tcp.h:22
char * name
Definition: dict.h:80
int flags
Definition: dict.h:81
#define DICT_ERR_RETRY
Definition: dict.h:178
DICT * dict_tcp_open(const char *map, int open_flags, int dict_flags)
Definition: dict_tcp.c:285
int vstring_get_nonl_bound(VSTRING *vp, VSTREAM *fp, ssize_t bound)
#define DICT_ERR_NONE
Definition: dict.h:177
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
#define RETURN(errval, result)
Definition: dict.h:78
#define ISDIGIT(c)
Definition: sys_defs.h:1748
VSTREAM * vstream_fprintf(VSTREAM *stream, const char *fmt,...)
Definition: vstream.c:1348
int inet_connect(const char *, int, int)
Definition: inet_connect.c:77
DICT dict
Definition: dict_tcp.c:103
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
VSTRING * hex_unquote(VSTRING *raw, const char *hex)
Definition: hex_quote.c:77
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
VSTRING * hex_quote(VSTRING *hex, const char *raw)
Definition: hex_quote.c:58
char * lowercase(char *string)
Definition: lowercase.c:34
const char *(* lookup)(struct DICT *, const char *)
Definition: dict.h:82
#define DICT_FLAG_PATTERN
Definition: dict.h:115
void dict_free(DICT *)
Definition: dict_alloc.c:163
#define DICT_TCP_MAXLEN
Definition: dict_tcp.c:111
#define NON_BLOCKING
Definition: iostuff.h:49
#define DICT_TCP_TMOUT
Definition: dict_tcp.c:110
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define DICT_FLAG_FOLD_MUL
Definition: dict.h:125
#define CA_VSTREAM_CTL_END
Definition: vstream.h:155
#define ISSPACE(c)
Definition: sys_defs.h:1753
DICT * dict_alloc(const char *, const char *, ssize_t)
Definition: dict_alloc.c:135
VSTRING * fold_buf
Definition: dict.h:92
char * printable(char *string, int replacement)
Definition: printable.c:49
void vstream_control(VSTREAM *stream, int name,...)
Definition: vstream.c:1372
VSTRING * raw_buf
Definition: dict_tcp.c:104
#define DICT_TCP_MAXTRY
Definition: dict_tcp.c:109
VSTREAM * vstream_fdopen(int fd, int flags)
Definition: vstream.c:1204
DICT * dict_surrogate(const char *dict_type, const char *dict_name, int open_flags, int dict_flags, const char *fmt,...)
void msg_info(const char *fmt,...)
Definition: msg.c:199