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 /*	Wietse Venema
61 /*	Google, Inc.
62 /*	111 8th Avenue
63 /*	New York, NY 10011, USA
64 /*--*/
65 
66 /* System library. */
67 
68 #include <sys_defs.h>
69 #include <unistd.h>
70 #include <errno.h>
71 
72 /* Utility library. */
73 
74 #include <msg.h>
75 #include <vstream.h>
76 #include <vstring.h>
77 #include <attr.h>
78 
79 /* Global library. */
80 
81 #include <mail_params.h>
82 #include <mail_proto.h>
83 #include <clnt_stream.h>
84 #include <verify_clnt.h>
85 
86 CLNT_STREAM *vrfy_clnt;
87 
88 /* verify_clnt_handshake - receive server protocol announcement */
89 
verify_clnt_handshake(VSTREAM * stream)90 static int verify_clnt_handshake(VSTREAM *stream)
91 {
92     return (attr_scan(stream, ATTR_FLAG_STRICT,
93 		   RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_VERIFY),
94 		      ATTR_TYPE_END));
95 }
96 
97 /* verify_clnt_init - initialize */
98 
verify_clnt_init(void)99 static void verify_clnt_init(void)
100 {
101     if (vrfy_clnt != 0)
102 	msg_panic("verify_clnt_init: multiple initialization");
103     vrfy_clnt = clnt_stream_create(MAIL_CLASS_PRIVATE, var_verify_service,
104 				   var_ipc_idle_limit, var_ipc_ttl_limit,
105 				   verify_clnt_handshake);
106 }
107 
108 /* verify_clnt_query - request address verification status */
109 
verify_clnt_query(const char * addr,int * addr_status,VSTRING * why)110 int     verify_clnt_query(const char *addr, int *addr_status, VSTRING *why)
111 {
112     VSTREAM *stream;
113     int     request_status;
114     int     count = 0;
115 
116     /*
117      * Do client-server plumbing.
118      */
119     if (vrfy_clnt == 0)
120 	verify_clnt_init();
121 
122     /*
123      * Request status for this address.
124      */
125     for (;;) {
126 	stream = clnt_stream_access(vrfy_clnt);
127 	errno = 0;
128 	count += 1;
129 	if (stream == 0
130 	    || attr_print(stream, ATTR_FLAG_NONE,
131 			  SEND_ATTR_STR(MAIL_ATTR_REQ, VRFY_REQ_QUERY),
132 			  SEND_ATTR_STR(MAIL_ATTR_ADDR, addr),
133 			  ATTR_TYPE_END) != 0
134 	    || vstream_fflush(stream)
135 	    || attr_scan(stream, ATTR_FLAG_MISSING,
136 			 RECV_ATTR_INT(MAIL_ATTR_STATUS, &request_status),
137 			 RECV_ATTR_INT(MAIL_ATTR_ADDR_STATUS, addr_status),
138 			 RECV_ATTR_STR(MAIL_ATTR_WHY, why),
139 			 ATTR_TYPE_END) != 3) {
140 	    if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
141 		msg_warn("problem talking to service %s: %m",
142 			 var_verify_service);
143 	} else {
144 	    break;
145 	}
146 	sleep(1);
147 	clnt_stream_recover(vrfy_clnt);
148     }
149     return (request_status);
150 }
151 
152 /* verify_clnt_update - request address status update */
153 
verify_clnt_update(const char * addr,int addr_status,const char * why)154 int     verify_clnt_update(const char *addr, int addr_status, const char *why)
155 {
156     VSTREAM *stream;
157     int     request_status;
158 
159     /*
160      * Do client-server plumbing.
161      */
162     if (vrfy_clnt == 0)
163 	verify_clnt_init();
164 
165     /*
166      * Send status for this address. Supply a default status if the address
167      * verification service is unavailable.
168      */
169     for (;;) {
170 	stream = clnt_stream_access(vrfy_clnt);
171 	errno = 0;
172 	if (stream == 0
173 	    || attr_print(stream, ATTR_FLAG_NONE,
174 			  SEND_ATTR_STR(MAIL_ATTR_REQ, VRFY_REQ_UPDATE),
175 			  SEND_ATTR_STR(MAIL_ATTR_ADDR, addr),
176 			  SEND_ATTR_INT(MAIL_ATTR_ADDR_STATUS, addr_status),
177 			  SEND_ATTR_STR(MAIL_ATTR_WHY, why),
178 			  ATTR_TYPE_END) != 0
179 	    || attr_scan(stream, ATTR_FLAG_MISSING,
180 			 RECV_ATTR_INT(MAIL_ATTR_STATUS, &request_status),
181 			 ATTR_TYPE_END) != 1) {
182 	    if (msg_verbose || (errno != EPIPE && errno != ENOENT))
183 		msg_warn("problem talking to service %s: %m",
184 			 var_verify_service);
185 	} else {
186 	    break;
187 	}
188 	sleep(1);
189 	clnt_stream_recover(vrfy_clnt);
190     }
191     return (request_status);
192 }
193 
194  /*
195   * Proof-of-concept test client program.
196   */
197 #ifdef TEST
198 
199 #include <stdlib.h>
200 #include <ctype.h>
201 #include <stdlib.h>
202 #include <unistd.h>
203 #include <signal.h>
204 #include <msg_vstream.h>
205 #include <stringops.h>
206 #include <vstring_vstream.h>
207 #include <mail_conf.h>
208 
209 #define STR(x) vstring_str(x)
210 
usage(char * myname)211 static NORETURN usage(char *myname)
212 {
213     msg_fatal("usage: %s [-v]", myname);
214 }
215 
query(char * query,VSTRING * buf)216 static void query(char *query, VSTRING *buf)
217 {
218     int     status;
219 
220     switch (verify_clnt_query(query, &status, buf)) {
221     case VRFY_STAT_OK:
222 	vstream_printf("%-10s %d\n", "status", status);
223 	vstream_printf("%-10s %s\n", "text", STR(buf));
224 	vstream_fflush(VSTREAM_OUT);
225 	break;
226     case VRFY_STAT_BAD:
227 	msg_warn("bad request format");
228 	break;
229     case VRFY_STAT_FAIL:
230 	msg_warn("request failed");
231 	break;
232     }
233 }
234 
update(char * query)235 static void update(char *query)
236 {
237     char   *addr;
238     char   *status_text;
239     char   *cp = query;
240 
241     if ((addr = mystrtok(&cp, CHARS_SPACE)) == 0
242 	|| (status_text = mystrtok(&cp, CHARS_SPACE)) == 0) {
243 	msg_warn("bad request format");
244 	return;
245     }
246     while (*cp && ISSPACE(*cp))
247 	cp++;
248     if (*cp == 0) {
249 	msg_warn("bad request format");
250 	return;
251     }
252     switch (verify_clnt_update(query, atoi(status_text), cp)) {
253     case VRFY_STAT_OK:
254 	vstream_printf("OK\n");
255 	vstream_fflush(VSTREAM_OUT);
256 	break;
257     case VRFY_STAT_BAD:
258 	msg_warn("bad request format");
259 	break;
260     case VRFY_STAT_FAIL:
261 	msg_warn("request failed");
262 	break;
263     }
264 }
265 
main(int argc,char ** argv)266 int     main(int argc, char **argv)
267 {
268     VSTRING *buffer = vstring_alloc(1);
269     char   *cp;
270     int     ch;
271     char   *command;
272 
273     signal(SIGPIPE, SIG_IGN);
274 
275     msg_vstream_init(argv[0], VSTREAM_ERR);
276 
277     mail_conf_read();
278     msg_info("using config files in %s", var_config_dir);
279     if (chdir(var_queue_dir) < 0)
280 	msg_fatal("chdir %s: %m", var_queue_dir);
281 
282     while ((ch = GETOPT(argc, argv, "v")) > 0) {
283 	switch (ch) {
284 	case 'v':
285 	    msg_verbose++;
286 	    break;
287 	default:
288 	    usage(argv[0]);
289 	}
290     }
291     if (argc - optind > 1)
292 	usage(argv[0]);
293 
294     while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
295 	cp = STR(buffer);
296 	if ((command = mystrtok(&cp, CHARS_SPACE)) == 0)
297 	    continue;
298 	if (strcmp(command, "query") == 0)
299 	    query(cp, buffer);
300 	else if (strcmp(command, "update") == 0)
301 	    update(cp);
302 	else
303 	    msg_warn("unrecognized command: %s", command);
304     }
305     vstring_free(buffer);
306     return (0);
307 }
308 
309 #endif
310