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