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 
88 CLNT_STREAM *vrfy_clnt;
89 
90 /* verify_clnt_handshake - receive server protocol announcement */
91 
verify_clnt_handshake(VSTREAM * stream)92 static 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 
verify_clnt_init(void)101 static 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 
verify_clnt_query(const char * addr,int * addr_status,VSTRING * why)112 int     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 
verify_clnt_update(const char * addr,int addr_status,const char * why)156 int     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 
usage(char * myname)213 static NORETURN usage(char *myname)
214 {
215     msg_fatal("usage: %s [-v]", myname);
216 }
217 
query(char * query,VSTRING * buf)218 static 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 
update(char * query)237 static 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 
main(int argc,char ** argv)268 int     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