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