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