1 /*++
2 /* NAME
3 /* attr_clnt 3
4 /* SUMMARY
5 /* attribute query-reply client
6 /* SYNOPSIS
7 /* #include <attr_clnt.h>
8 /*
9 /* typedef int (*ATTR_CLNT_PRINT_FN) (VSTREAM *, int, va_list);
10 /* typedef int (*ATTR_CLNT_SCAN_FN) (VSTREAM *, int, va_list);
11 /* typedef int (*ATTR_CLNT_HANDSHAKE_FN) (VSTREAM *);
12 /*
13 /* ATTR_CLNT *attr_clnt_create(server, timeout, max_idle, max_ttl)
14 /* const char *server;
15 /* int timeout;
16 /* int max_idle;
17 /* int max_ttl;
18 /*
19 /* int attr_clnt_request(client,
20 /* send_flags, send_type, send_name, ..., ATTR_TYPE_END,
21 /* recv_flags, recv_type, recv_name, ..., ATTR_TYPE_END)
22 /* ATTR_CLNT *client;
23 /* int send_flags;
24 /* int send_type;
25 /* const char *send_name;
26 /* int recv_flags;
27 /* int recv_type;
28 /* const char *recv_name;
29 /*
30 /* void attr_clnt_free(client)
31 /* ATTR_CLNT *client;
32 /*
33 /* void attr_clnt_control(client, name, value, ... ATTR_CLNT_CTL_END)
34 /* ATTR_CLNT *client;
35 /* int name;
36 /* DESCRIPTION
37 /* This module implements a client for a simple attribute-based
38 /* protocol. The default protocol is described in attr_scan_plain(3).
39 /*
40 /* attr_clnt_create() creates a client handle. See auto_clnt(3) for
41 /* a description of the arguments.
42 /*
43 /* attr_clnt_request() sends the specified request attributes and
44 /* receives a reply. The reply argument specifies a name-value table.
45 /* The other arguments are as described in attr_print_plain(3). The
46 /* result is the number of attributes received or -1 in case of trouble.
47 /*
48 /* attr_clnt_free() destroys a client handle and closes its connection.
49 /*
50 /* attr_clnt_control() allows the user to fine tune the behavior of
51 /* the specified client. The arguments are a list of (name, value)
52 /* terminated with ATTR_CLNT_CTL_END.
53 /* The following lists the names and the types of the corresponding
54 /* value arguments.
55 /* .IP "ATTR_CLNT_CTL_PROTO(ATTR_CLNT_PRINT_FN, ATTR_CLNT_SCAN_FN)"
56 /* Specifies alternatives for the attr_plain_print() and
57 /* attr_plain_scan() functions.
58 /* .IP "ATTR_CLNT_CTL_REQ_LIMIT(int)"
59 /* The maximal number of requests per connection (default: 0,
60 /* i.e. no limit). To enable the limit, specify a value greater
61 /* than zero.
62 /* .IP "ATTR_CLNT_CTL_TRY_LIMIT(int)"
63 /* The maximal number of attempts to send a request before
64 /* giving up (default: 2). To disable the limit, specify a
65 /* value equal to zero.
66 /* .IP "ATTR_CLNT_CTL_TRY_DELAY(int)"
67 /* The time in seconds between attempts to send a request
68 /* (default: 1). Specify a value greater than zero.
69 /* .IP "ATTR_CLNT_CTL_HANDSHAKE(VSTREAM *)"
70 /* A pointer to function that will be called at the start of a
71 /* new connection, and that returns 0 in case of success.
72 /* DIAGNOSTICS
73 /* Warnings: communication failure.
74 /* SEE ALSO
75 /* auto_clnt(3), client endpoint management
76 /* attr_scan_plain(3), attribute protocol
77 /* attr_print_plain(3), attribute protocol
78 /* LICENSE
79 /* .ad
80 /* .fi
81 /* The Secure Mailer license must be distributed with this software.
82 /* AUTHOR(S)
83 /* Wietse Venema
84 /* IBM T.J. Watson Research
85 /* P.O. Box 704
86 /* Yorktown Heights, NY 10598, USA
87 /*
88 /* Wietse Venema
89 /* Google, Inc.
90 /* 111 8th Avenue
91 /* New York, NY 10011, USA
92 /*--*/
93
94 /* System library. */
95
96 #include <sys_defs.h>
97 #include <unistd.h>
98 #include <errno.h>
99
100 /* Utility library. */
101
102 #include <msg.h>
103 #include <mymalloc.h>
104 #include <vstream.h>
105 #include <htable.h>
106 #include <attr.h>
107 #include <iostuff.h>
108 #include <compat_va_copy.h>
109 #include <auto_clnt.h>
110 #include <attr_clnt.h>
111
112 /* Application-specific. */
113
114 struct ATTR_CLNT {
115 AUTO_CLNT *auto_clnt;
116 /* Remaining properties are set with attr_clnt_control(). */
117 ATTR_CLNT_PRINT_FN print;
118 ATTR_CLNT_SCAN_FN scan;
119 int req_limit;
120 int req_count;
121 int try_limit;
122 int try_delay;
123 };
124
125 #define ATTR_CLNT_DEF_REQ_LIMIT (0) /* default per-session request limit */
126 #define ATTR_CLNT_DEF_TRY_LIMIT (2) /* default request (re)try limit */
127 #define ATTR_CLNT_DEF_TRY_DELAY (1) /* default request (re)try delay */
128
129 /* attr_clnt_free - destroy attribute client */
130
attr_clnt_free(ATTR_CLNT * client)131 void attr_clnt_free(ATTR_CLNT *client)
132 {
133 auto_clnt_free(client->auto_clnt);
134 myfree((void *) client);
135 }
136
137 /* attr_clnt_create - create attribute client */
138
attr_clnt_create(const char * service,int timeout,int max_idle,int max_ttl)139 ATTR_CLNT *attr_clnt_create(const char *service, int timeout,
140 int max_idle, int max_ttl)
141 {
142 ATTR_CLNT *client;
143
144 client = (ATTR_CLNT *) mymalloc(sizeof(*client));
145 client->auto_clnt = auto_clnt_create(service, timeout, max_idle, max_ttl);
146 client->scan = attr_vscan_plain;
147 client->print = attr_vprint_plain;
148 client->req_limit = ATTR_CLNT_DEF_REQ_LIMIT;
149 client->req_count = 0;
150 client->try_limit = ATTR_CLNT_DEF_TRY_LIMIT;
151 client->try_delay = ATTR_CLNT_DEF_TRY_DELAY;
152 return (client);
153 }
154
155 /* attr_clnt_request - send query, receive reply */
156
attr_clnt_request(ATTR_CLNT * client,int send_flags,...)157 int attr_clnt_request(ATTR_CLNT *client, int send_flags,...)
158 {
159 const char *myname = "attr_clnt_request";
160 VSTREAM *stream;
161 int count = 0;
162 va_list saved_ap;
163 va_list ap;
164 int type;
165 int recv_flags;
166 int err;
167 int ret;
168
169 /*
170 * XXX If the stream is readable before we send anything, then assume the
171 * remote end disconnected.
172 *
173 * XXX For some reason we can't simply call the scan routine after the print
174 * routine, that messes up the argument list.
175 */
176 #define SKIP_ARG(ap, type) { \
177 (void) va_arg(ap, char *); \
178 (void) va_arg(ap, type); \
179 }
180 #define SKIP_ARG2(ap, t1, t2) { \
181 SKIP_ARG(ap, t1); \
182 (void) va_arg(ap, t2); \
183 }
184
185 /* Finalize argument lists before returning. */
186 va_start(saved_ap, send_flags);
187 for (;;) {
188 errno = 0;
189 if ((stream = auto_clnt_access(client->auto_clnt)) != 0
190 && readable(vstream_fileno(stream)) == 0) {
191 errno = 0;
192 VA_COPY(ap, saved_ap);
193 err = (client->print(stream, send_flags, ap) != 0
194 || vstream_fflush(stream) != 0);
195 va_end(ap);
196 if (err == 0) {
197 VA_COPY(ap, saved_ap);
198 while ((type = va_arg(ap, int)) != ATTR_TYPE_END) {
199 switch (type) {
200 case ATTR_TYPE_STR:
201 SKIP_ARG(ap, char *);
202 break;
203 case ATTR_TYPE_DATA:
204 SKIP_ARG2(ap, ssize_t, char *);
205 break;
206 case ATTR_TYPE_INT:
207 SKIP_ARG(ap, int);
208 break;
209 case ATTR_TYPE_LONG:
210 SKIP_ARG(ap, long);
211 break;
212 case ATTR_TYPE_HASH:
213 (void) va_arg(ap, HTABLE *);
214 break;
215 default:
216 msg_panic("%s: unexpected attribute type %d",
217 myname, type);
218 }
219 }
220 recv_flags = va_arg(ap, int);
221 ret = client->scan(stream, recv_flags, ap);
222 va_end(ap);
223 /* Finalize argument lists before returning. */
224 if (ret > 0) {
225 if (client->req_limit > 0
226 && (client->req_count += 1) >= client->req_limit) {
227 auto_clnt_recover(client->auto_clnt);
228 client->req_count = 0;
229 }
230 break;
231 }
232 }
233 }
234 if ((++count >= client->try_limit && client->try_limit > 0)
235 || msg_verbose
236 || (errno && errno != EPIPE && errno != ENOENT && errno != ECONNRESET))
237 msg_warn("problem talking to server %s: %m",
238 auto_clnt_name(client->auto_clnt));
239 /* Finalize argument lists before returning. */
240 if (count >= client->try_limit && client->try_limit > 0) {
241 ret = -1;
242 break;
243 }
244 sleep(client->try_delay);
245 auto_clnt_recover(client->auto_clnt);
246 client->req_count = 0;
247 }
248 /* Finalize argument lists before returning. */
249 va_end(saved_ap);
250 return (ret);
251 }
252
253 /* attr_clnt_control - fine control */
254
attr_clnt_control(ATTR_CLNT * client,int name,...)255 void attr_clnt_control(ATTR_CLNT *client, int name,...)
256 {
257 const char *myname = "attr_clnt_control";
258 va_list ap;
259
260 for (va_start(ap, name); name != ATTR_CLNT_CTL_END; name = va_arg(ap, int)) {
261 switch (name) {
262 case ATTR_CLNT_CTL_PROTO:
263 client->print = va_arg(ap, ATTR_CLNT_PRINT_FN);
264 client->scan = va_arg(ap, ATTR_CLNT_SCAN_FN);
265 break;
266 case ATTR_CLNT_CTL_HANDSHAKE:
267 auto_clnt_control(client->auto_clnt,
268 AUTO_CLNT_CTL_HANDSHAKE,
269 va_arg(ap, ATTR_CLNT_HANDSHAKE_FN),
270 AUTO_CLNT_CTL_END);
271 break;
272 case ATTR_CLNT_CTL_REQ_LIMIT:
273 client->req_limit = va_arg(ap, int);
274 if (client->req_limit < 0)
275 msg_panic("%s: bad request limit: %d",
276 myname, client->req_limit);
277 if (msg_verbose)
278 msg_info("%s: new request limit %d",
279 myname, client->req_limit);
280 break;
281 case ATTR_CLNT_CTL_TRY_LIMIT:
282 client->try_limit = va_arg(ap, int);
283 if (client->try_limit < 0)
284 msg_panic("%s: bad retry limit: %d", myname, client->try_limit);
285 if (msg_verbose)
286 msg_info("%s: new retry limit %d", myname, client->try_limit);
287 break;
288 case ATTR_CLNT_CTL_TRY_DELAY:
289 client->try_delay = va_arg(ap, int);
290 if (client->try_delay <= 0)
291 msg_panic("%s: bad retry delay: %d", myname, client->try_delay);
292 if (msg_verbose)
293 msg_info("%s: new retry delay %d", myname, client->try_delay);
294 break;
295 default:
296 msg_panic("%s: bad name %d", myname, name);
297 }
298 }
299 va_end(ap);
300 }
301