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