1/*++
2/* NAME
3/*	verify_clnt 3
4/* SUMMARY
5/*	address verification client interface
6/* SYNOPSIS
7/*	#include <verify_clnt.h>
8/*
9/*	int	verify_clnt_query(addr, status, why)
10/*	const char *addr;
11/*	int	*status;
12/*	VSTRING	*why;
13/*
14/*	int	verify_clnt_update(addr, status, why)
15/*	const char *addr;
16/*	int	status;
17/*	const char *why;
18/* DESCRIPTION
19/*	verify_clnt_query() requests information about the given address.
20/*	The result value is one of the valid status values (see
21/*	status description below).
22/*	In all cases the \fBwhy\fR argument provides additional
23/*	information.
24/*
25/*	verify_clnt_update() requests that the status of the specified
26/*	address be updated. The result status is DEL_REQ_RCPT_STAT_OK upon
27/*	success, DEL_REQ_RCPT_STAT_DEFER upon failure.
28/*
29/*	Arguments
30/* .IP addr
31/*	The email address in question.
32/* .IP status
33/*	One of the following status codes:
34/* .RS
35/* .IP DEL_REQ_RCPT_STAT_OK
36/*	The mail system did not detect any problems.
37/* .IP DEL_REQ_RCPT_STAT_DEFER
38/*	The status of the address is indeterminate.
39/* .IP DEL_REQ_RCPT_STAT_BOUNCE
40/*	The address is permanently undeliverable.
41/* .RE
42/* .IP why
43/*	textual description of the status.
44/* DIAGNOSTICS
45/*	These functions return VRFY_STAT_OK in case of success,
46/*	VRFY_STAT_BAD in case of a malformed request, and
47/*	VRFY_STAT_FAIL when the operation failed.
48/* SEE ALSO
49/*	verify(8) Postfix address verification server
50/* LICENSE
51/* .ad
52/* .fi
53/*	The Secure Mailer license must be distributed with this software.
54/* AUTHOR(S)
55/*	Wietse Venema
56/*	IBM T.J. Watson Research
57/*	P.O. Box 704
58/*	Yorktown Heights, NY 10598, USA
59/*--*/
60
61/* System library. */
62
63#include <sys_defs.h>
64#include <unistd.h>
65#include <errno.h>
66
67/* Utility library. */
68
69#include <msg.h>
70#include <vstream.h>
71#include <vstring.h>
72#include <attr.h>
73
74/* Global library. */
75
76#include <mail_params.h>
77#include <mail_proto.h>
78#include <clnt_stream.h>
79#include <verify_clnt.h>
80
81CLNT_STREAM *vrfy_clnt;
82
83/* verify_clnt_init - initialize */
84
85static void verify_clnt_init(void)
86{
87    if (vrfy_clnt != 0)
88	msg_panic("verify_clnt_init: multiple initialization");
89    vrfy_clnt = clnt_stream_create(MAIL_CLASS_PRIVATE, var_verify_service,
90				   var_ipc_idle_limit, var_ipc_ttl_limit);
91}
92
93/* verify_clnt_query - request address verification status */
94
95int     verify_clnt_query(const char *addr, int *addr_status, VSTRING *why)
96{
97    VSTREAM *stream;
98    int     request_status;
99    int     count = 0;
100
101    /*
102     * Do client-server plumbing.
103     */
104    if (vrfy_clnt == 0)
105	verify_clnt_init();
106
107    /*
108     * Request status for this address.
109     */
110    for (;;) {
111	stream = clnt_stream_access(vrfy_clnt);
112	errno = 0;
113	count += 1;
114	if (attr_print(stream, ATTR_FLAG_NONE,
115		       ATTR_TYPE_STR, MAIL_ATTR_REQ, VRFY_REQ_QUERY,
116		       ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
117		       ATTR_TYPE_END) != 0
118	    || vstream_fflush(stream)
119	    || attr_scan(stream, ATTR_FLAG_MISSING,
120			 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &request_status,
121			 ATTR_TYPE_INT, MAIL_ATTR_ADDR_STATUS, addr_status,
122			 ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
123			 ATTR_TYPE_END) != 3) {
124	    if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
125		msg_warn("problem talking to service %s: %m",
126			 var_verify_service);
127	} else {
128	    break;
129	}
130	sleep(1);
131	clnt_stream_recover(vrfy_clnt);
132    }
133    return (request_status);
134}
135
136/* verify_clnt_update - request address status update */
137
138int     verify_clnt_update(const char *addr, int addr_status, const char *why)
139{
140    VSTREAM *stream;
141    int     request_status;
142
143    /*
144     * Do client-server plumbing.
145     */
146    if (vrfy_clnt == 0)
147	verify_clnt_init();
148
149    /*
150     * Send status for this address. Supply a default status if the address
151     * verification service is unavailable.
152     */
153    for (;;) {
154	stream = clnt_stream_access(vrfy_clnt);
155	errno = 0;
156	if (attr_print(stream, ATTR_FLAG_NONE,
157		       ATTR_TYPE_STR, MAIL_ATTR_REQ, VRFY_REQ_UPDATE,
158		       ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
159		       ATTR_TYPE_INT, MAIL_ATTR_ADDR_STATUS, addr_status,
160		       ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
161		       ATTR_TYPE_END) != 0
162	    || attr_scan(stream, ATTR_FLAG_MISSING,
163			 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &request_status,
164			 ATTR_TYPE_END) != 1) {
165	    if (msg_verbose || (errno != EPIPE && errno != ENOENT))
166		msg_warn("problem talking to service %s: %m",
167			 var_verify_service);
168	} else {
169	    break;
170	}
171	sleep(1);
172	clnt_stream_recover(vrfy_clnt);
173    }
174    return (request_status);
175}
176
177 /*
178  * Proof-of-concept test client program.
179  */
180#ifdef TEST
181
182#include <stdlib.h>
183#include <ctype.h>
184#include <stdlib.h>
185#include <unistd.h>
186#include <signal.h>
187#include <msg_vstream.h>
188#include <stringops.h>
189#include <vstring_vstream.h>
190#include <mail_conf.h>
191
192#define STR(x) vstring_str(x)
193
194static NORETURN usage(char *myname)
195{
196    msg_fatal("usage: %s [-v]", myname);
197}
198
199static void query(char *query, VSTRING *buf)
200{
201    int     status;
202
203    switch (verify_clnt_query(query, &status, buf)) {
204    case VRFY_STAT_OK:
205	vstream_printf("%-10s %d\n", "status", status);
206	vstream_printf("%-10s %s\n", "text", STR(buf));
207	vstream_fflush(VSTREAM_OUT);
208	break;
209    case VRFY_STAT_BAD:
210	msg_warn("bad request format");
211	break;
212    case VRFY_STAT_FAIL:
213	msg_warn("request failed");
214	break;
215    }
216}
217
218static void update(char *query)
219{
220    char   *addr;
221    char   *status_text;
222    char   *cp = query;
223
224    if ((addr = mystrtok(&cp, " \t\r\n")) == 0
225	|| (status_text = mystrtok(&cp, " \t\r\n")) == 0) {
226	msg_warn("bad request format");
227	return;
228    }
229    while (*cp && ISSPACE(*cp))
230	cp++;
231    if (*cp == 0) {
232	msg_warn("bad request format");
233	return;
234    }
235    switch (verify_clnt_update(query, atoi(status_text), cp)) {
236    case VRFY_STAT_OK:
237	vstream_printf("OK\n");
238	vstream_fflush(VSTREAM_OUT);
239	break;
240    case VRFY_STAT_BAD:
241	msg_warn("bad request format");
242	break;
243    case VRFY_STAT_FAIL:
244	msg_warn("request failed");
245	break;
246    }
247}
248
249int     main(int argc, char **argv)
250{
251    VSTRING *buffer = vstring_alloc(1);
252    char   *cp;
253    int     ch;
254    char   *command;
255
256    signal(SIGPIPE, SIG_IGN);
257
258    msg_vstream_init(argv[0], VSTREAM_ERR);
259
260    mail_conf_read();
261    msg_info("using config files in %s", var_config_dir);
262    if (chdir(var_queue_dir) < 0)
263	msg_fatal("chdir %s: %m", var_queue_dir);
264
265    while ((ch = GETOPT(argc, argv, "v")) > 0) {
266	switch (ch) {
267	case 'v':
268	    msg_verbose++;
269	    break;
270	default:
271	    usage(argv[0]);
272	}
273    }
274    if (argc - optind > 1)
275	usage(argv[0]);
276
277    while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
278	cp = STR(buffer);
279	if ((command = mystrtok(&cp, " \t\r\n")) == 0)
280	    continue;
281	if (strcmp(command, "query") == 0)
282	    query(cp, buffer);
283	else if (strcmp(command, "update") == 0)
284	    update(cp);
285	else
286	    msg_warn("unrecognized command: %s", command);
287    }
288    vstring_free(buffer);
289    return (0);
290}
291
292#endif
293