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