1 |
|
|
/* $OpenBSD: smtpctl.c,v 1.154 2017/07/27 18:48:30 sunil Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2013 Eric Faurot <eric@openbsd.org> |
5 |
|
|
* Copyright (c) 2006 Gilles Chehade <gilles@poolp.org> |
6 |
|
|
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> |
7 |
|
|
* Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> |
8 |
|
|
* Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> |
9 |
|
|
* Copyright (c) 2003 Henning Brauer <henning@openbsd.org> |
10 |
|
|
* |
11 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
12 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
13 |
|
|
* copyright notice and this permission notice appear in all copies. |
14 |
|
|
* |
15 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
16 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
17 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
18 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
19 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
20 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
21 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
22 |
|
|
*/ |
23 |
|
|
|
24 |
|
|
#include <sys/types.h> |
25 |
|
|
#include <sys/socket.h> |
26 |
|
|
#include <sys/queue.h> |
27 |
|
|
#include <sys/tree.h> |
28 |
|
|
#include <sys/un.h> |
29 |
|
|
#include <sys/wait.h> |
30 |
|
|
#include <sys/stat.h> |
31 |
|
|
|
32 |
|
|
#include <err.h> |
33 |
|
|
#include <errno.h> |
34 |
|
|
#include <event.h> |
35 |
|
|
#include <fts.h> |
36 |
|
|
#include <imsg.h> |
37 |
|
|
#include <inttypes.h> |
38 |
|
|
#include <pwd.h> |
39 |
|
|
#include <stdio.h> |
40 |
|
|
#include <stdlib.h> |
41 |
|
|
#include <string.h> |
42 |
|
|
#include <time.h> |
43 |
|
|
#include <unistd.h> |
44 |
|
|
#include <limits.h> |
45 |
|
|
|
46 |
|
|
#include "smtpd.h" |
47 |
|
|
#include "parser.h" |
48 |
|
|
#include "log.h" |
49 |
|
|
|
50 |
|
|
#define PATH_GZCAT "/usr/bin/gzcat" |
51 |
|
|
#define PATH_CAT "/bin/cat" |
52 |
|
|
#define PATH_QUEUE "/queue" |
53 |
|
|
#define PATH_ENCRYPT "/usr/bin/encrypt" |
54 |
|
|
|
55 |
|
|
int srv_connect(void); |
56 |
|
|
int srv_connected(void); |
57 |
|
|
|
58 |
|
|
void usage(void); |
59 |
|
|
static void show_queue_envelope(struct envelope *, int); |
60 |
|
|
static void getflag(uint *, int, char *, char *, size_t); |
61 |
|
|
static void display(const char *); |
62 |
|
|
static int str_to_trace(const char *); |
63 |
|
|
static int str_to_profile(const char *); |
64 |
|
|
static void show_offline_envelope(uint64_t); |
65 |
|
|
static int is_gzip_fp(FILE *); |
66 |
|
|
static int is_encrypted_fp(FILE *); |
67 |
|
|
static int is_encrypted_buffer(const char *); |
68 |
|
|
static int is_gzip_buffer(const char *); |
69 |
|
|
static FILE *offline_file(void); |
70 |
|
|
static void sendmail_compat(int, char **); |
71 |
|
|
|
72 |
|
|
extern char *__progname; |
73 |
|
|
int sendmail; |
74 |
|
|
struct smtpd *env; |
75 |
|
|
struct imsgbuf *ibuf; |
76 |
|
|
struct imsg imsg; |
77 |
|
|
char *rdata; |
78 |
|
|
size_t rlen; |
79 |
|
|
time_t now; |
80 |
|
|
|
81 |
|
|
struct queue_backend queue_backend_null; |
82 |
|
|
struct queue_backend queue_backend_proc; |
83 |
|
|
struct queue_backend queue_backend_ram; |
84 |
|
|
|
85 |
|
|
__dead void |
86 |
|
|
usage(void) |
87 |
|
|
{ |
88 |
|
|
if (sendmail) |
89 |
|
|
fprintf(stderr, "usage: %s [-tv] [-f from] [-F name] to ...\n", |
90 |
|
|
__progname); |
91 |
|
|
else |
92 |
|
|
fprintf(stderr, "usage: %s command [argument ...]\n", |
93 |
|
|
__progname); |
94 |
|
|
exit(1); |
95 |
|
|
} |
96 |
|
|
|
97 |
|
|
void stat_increment(const char *k, size_t v) |
98 |
|
|
{ |
99 |
|
|
} |
100 |
|
|
|
101 |
|
|
void stat_decrement(const char *k, size_t v) |
102 |
|
|
{ |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
int |
106 |
|
|
srv_connect(void) |
107 |
|
|
{ |
108 |
|
|
struct sockaddr_un s_un; |
109 |
|
|
int ctl_sock, saved_errno; |
110 |
|
|
|
111 |
|
|
/* connect to smtpd control socket */ |
112 |
|
|
if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) |
113 |
|
|
err(1, "socket"); |
114 |
|
|
|
115 |
|
|
memset(&s_un, 0, sizeof(s_un)); |
116 |
|
|
s_un.sun_family = AF_UNIX; |
117 |
|
|
(void)strlcpy(s_un.sun_path, SMTPD_SOCKET, sizeof(s_un.sun_path)); |
118 |
|
|
if (connect(ctl_sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { |
119 |
|
|
saved_errno = errno; |
120 |
|
|
close(ctl_sock); |
121 |
|
|
errno = saved_errno; |
122 |
|
|
return (0); |
123 |
|
|
} |
124 |
|
|
|
125 |
|
|
ibuf = xcalloc(1, sizeof(struct imsgbuf), "smtpctl:srv_connect"); |
126 |
|
|
imsg_init(ibuf, ctl_sock); |
127 |
|
|
|
128 |
|
|
return (1); |
129 |
|
|
} |
130 |
|
|
|
131 |
|
|
int |
132 |
|
|
srv_connected(void) |
133 |
|
|
{ |
134 |
|
|
return ibuf != NULL ? 1 : 0; |
135 |
|
|
} |
136 |
|
|
|
137 |
|
|
FILE * |
138 |
|
|
offline_file(void) |
139 |
|
|
{ |
140 |
|
|
char path[PATH_MAX]; |
141 |
|
|
int fd; |
142 |
|
|
FILE *fp; |
143 |
|
|
|
144 |
|
|
if (!bsnprintf(path, sizeof(path), "%s%s/%lld.XXXXXXXXXX", PATH_SPOOL, |
145 |
|
|
PATH_OFFLINE, (long long int) time(NULL))) |
146 |
|
|
err(EX_UNAVAILABLE, "snprintf"); |
147 |
|
|
|
148 |
|
|
if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { |
149 |
|
|
if (fd != -1) |
150 |
|
|
unlink(path); |
151 |
|
|
err(EX_UNAVAILABLE, "cannot create temporary file %s", path); |
152 |
|
|
} |
153 |
|
|
|
154 |
|
|
if (fchmod(fd, 0600) == -1) { |
155 |
|
|
unlink(path); |
156 |
|
|
err(EX_SOFTWARE, "fchmod"); |
157 |
|
|
} |
158 |
|
|
|
159 |
|
|
return fp; |
160 |
|
|
} |
161 |
|
|
|
162 |
|
|
|
163 |
|
|
static void |
164 |
|
|
srv_flush(void) |
165 |
|
|
{ |
166 |
|
|
if (imsg_flush(ibuf) == -1) |
167 |
|
|
err(1, "write error"); |
168 |
|
|
} |
169 |
|
|
|
170 |
|
|
static void |
171 |
|
|
srv_send(int msg, const void *data, size_t len) |
172 |
|
|
{ |
173 |
|
|
if (ibuf == NULL && !srv_connect()) |
174 |
|
|
errx(1, "smtpd doesn't seem to be running"); |
175 |
|
|
imsg_compose(ibuf, msg, IMSG_VERSION, 0, -1, data, len); |
176 |
|
|
} |
177 |
|
|
|
178 |
|
|
static void |
179 |
|
|
srv_recv(int type) |
180 |
|
|
{ |
181 |
|
|
ssize_t n; |
182 |
|
|
|
183 |
|
|
srv_flush(); |
184 |
|
|
|
185 |
|
|
while (1) { |
186 |
|
|
if ((n = imsg_get(ibuf, &imsg)) == -1) |
187 |
|
|
errx(1, "imsg_get error"); |
188 |
|
|
if (n) { |
189 |
|
|
if (imsg.hdr.type == IMSG_CTL_FAIL && |
190 |
|
|
imsg.hdr.peerid != 0 && |
191 |
|
|
imsg.hdr.peerid != IMSG_VERSION) |
192 |
|
|
errx(1, "incompatible smtpctl and smtpd"); |
193 |
|
|
if (type != -1 && type != (int)imsg.hdr.type) |
194 |
|
|
errx(1, "bad message type"); |
195 |
|
|
rdata = imsg.data; |
196 |
|
|
rlen = imsg.hdr.len - sizeof(imsg.hdr); |
197 |
|
|
break; |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) |
201 |
|
|
errx(1, "imsg_read error"); |
202 |
|
|
if (n == 0) |
203 |
|
|
errx(1, "pipe closed"); |
204 |
|
|
} |
205 |
|
|
} |
206 |
|
|
|
207 |
|
|
static void |
208 |
|
|
srv_read(void *dst, size_t sz) |
209 |
|
|
{ |
210 |
|
|
if (sz == 0) |
211 |
|
|
return; |
212 |
|
|
if (rlen < sz) |
213 |
|
|
errx(1, "message too short"); |
214 |
|
|
if (dst) |
215 |
|
|
memmove(dst, rdata, sz); |
216 |
|
|
rlen -= sz; |
217 |
|
|
rdata += sz; |
218 |
|
|
} |
219 |
|
|
|
220 |
|
|
static void |
221 |
|
|
srv_get_int(int *i) |
222 |
|
|
{ |
223 |
|
|
srv_read(i, sizeof(*i)); |
224 |
|
|
} |
225 |
|
|
|
226 |
|
|
static void |
227 |
|
|
srv_get_time(time_t *t) |
228 |
|
|
{ |
229 |
|
|
srv_read(t, sizeof(*t)); |
230 |
|
|
} |
231 |
|
|
|
232 |
|
|
static void |
233 |
|
|
srv_get_evpid(uint64_t *evpid) |
234 |
|
|
{ |
235 |
|
|
srv_read(evpid, sizeof(*evpid)); |
236 |
|
|
} |
237 |
|
|
|
238 |
|
|
static void |
239 |
|
|
srv_get_string(const char **s) |
240 |
|
|
{ |
241 |
|
|
const char *end; |
242 |
|
|
size_t len; |
243 |
|
|
|
244 |
|
|
if (rlen == 0) |
245 |
|
|
errx(1, "message too short"); |
246 |
|
|
|
247 |
|
|
end = memchr(rdata, 0, rlen); |
248 |
|
|
if (end == NULL) |
249 |
|
|
errx(1, "unterminated string"); |
250 |
|
|
|
251 |
|
|
len = end + 1 - rdata; |
252 |
|
|
|
253 |
|
|
*s = rdata; |
254 |
|
|
rlen -= len; |
255 |
|
|
rdata += len; |
256 |
|
|
} |
257 |
|
|
|
258 |
|
|
static void |
259 |
|
|
srv_get_envelope(struct envelope *evp) |
260 |
|
|
{ |
261 |
|
|
uint64_t evpid; |
262 |
|
|
const char *str; |
263 |
|
|
|
264 |
|
|
srv_get_evpid(&evpid); |
265 |
|
|
srv_get_string(&str); |
266 |
|
|
|
267 |
|
|
envelope_load_buffer(evp, str, strlen(str)); |
268 |
|
|
evp->id = evpid; |
269 |
|
|
} |
270 |
|
|
|
271 |
|
|
static void |
272 |
|
|
srv_end(void) |
273 |
|
|
{ |
274 |
|
|
if (rlen) |
275 |
|
|
errx(1, "bogus data"); |
276 |
|
|
imsg_free(&imsg); |
277 |
|
|
} |
278 |
|
|
|
279 |
|
|
static int |
280 |
|
|
srv_check_result(int verbose_) |
281 |
|
|
{ |
282 |
|
|
srv_recv(-1); |
283 |
|
|
srv_end(); |
284 |
|
|
|
285 |
|
|
switch (imsg.hdr.type) { |
286 |
|
|
case IMSG_CTL_OK: |
287 |
|
|
if (verbose_) |
288 |
|
|
printf("command succeeded\n"); |
289 |
|
|
return (0); |
290 |
|
|
case IMSG_CTL_FAIL: |
291 |
|
|
if (verbose_) { |
292 |
|
|
if (rlen) |
293 |
|
|
printf("command failed: %s\n", rdata); |
294 |
|
|
else |
295 |
|
|
printf("command failed\n"); |
296 |
|
|
} |
297 |
|
|
return (1); |
298 |
|
|
default: |
299 |
|
|
errx(1, "wrong message in response: %u", imsg.hdr.type); |
300 |
|
|
} |
301 |
|
|
return (0); |
302 |
|
|
} |
303 |
|
|
|
304 |
|
|
static int |
305 |
|
|
srv_iter_messages(uint32_t *res) |
306 |
|
|
{ |
307 |
|
|
static uint32_t *msgids = NULL, from = 0; |
308 |
|
|
static size_t n, curr; |
309 |
|
|
static int done = 0; |
310 |
|
|
|
311 |
|
|
if (done) |
312 |
|
|
return (0); |
313 |
|
|
|
314 |
|
|
if (msgids == NULL) { |
315 |
|
|
srv_send(IMSG_CTL_LIST_MESSAGES, &from, sizeof(from)); |
316 |
|
|
srv_recv(IMSG_CTL_LIST_MESSAGES); |
317 |
|
|
if (rlen == 0) { |
318 |
|
|
srv_end(); |
319 |
|
|
done = 1; |
320 |
|
|
return (0); |
321 |
|
|
} |
322 |
|
|
msgids = malloc(rlen); |
323 |
|
|
n = rlen / sizeof(*msgids); |
324 |
|
|
srv_read(msgids, rlen); |
325 |
|
|
srv_end(); |
326 |
|
|
|
327 |
|
|
curr = 0; |
328 |
|
|
from = msgids[n - 1] + 1; |
329 |
|
|
if (from == 0) |
330 |
|
|
done = 1; |
331 |
|
|
} |
332 |
|
|
|
333 |
|
|
*res = msgids[curr++]; |
334 |
|
|
if (curr == n) { |
335 |
|
|
free(msgids); |
336 |
|
|
msgids = NULL; |
337 |
|
|
} |
338 |
|
|
|
339 |
|
|
return (1); |
340 |
|
|
} |
341 |
|
|
|
342 |
|
|
static int |
343 |
|
|
srv_iter_envelopes(uint32_t msgid, struct envelope *evp) |
344 |
|
|
{ |
345 |
|
|
static uint32_t currmsgid = 0; |
346 |
|
|
static uint64_t from = 0; |
347 |
|
|
static int done = 0, need_send = 1, found; |
348 |
|
|
int flags; |
349 |
|
|
time_t nexttry; |
350 |
|
|
|
351 |
|
|
if (currmsgid != msgid) { |
352 |
|
|
if (currmsgid != 0 && !done) |
353 |
|
|
errx(1, "must finish current iteration first"); |
354 |
|
|
currmsgid = msgid; |
355 |
|
|
from = msgid_to_evpid(msgid); |
356 |
|
|
done = 0; |
357 |
|
|
found = 0; |
358 |
|
|
need_send = 1; |
359 |
|
|
} |
360 |
|
|
|
361 |
|
|
if (done) |
362 |
|
|
return (0); |
363 |
|
|
|
364 |
|
|
again: |
365 |
|
|
if (need_send) { |
366 |
|
|
found = 0; |
367 |
|
|
srv_send(IMSG_CTL_LIST_ENVELOPES, &from, sizeof(from)); |
368 |
|
|
} |
369 |
|
|
need_send = 0; |
370 |
|
|
|
371 |
|
|
srv_recv(IMSG_CTL_LIST_ENVELOPES); |
372 |
|
|
if (rlen == 0) { |
373 |
|
|
srv_end(); |
374 |
|
|
if (!found || evpid_to_msgid(from) != msgid) { |
375 |
|
|
done = 1; |
376 |
|
|
return (0); |
377 |
|
|
} |
378 |
|
|
need_send = 1; |
379 |
|
|
goto again; |
380 |
|
|
} |
381 |
|
|
|
382 |
|
|
srv_get_int(&flags); |
383 |
|
|
srv_get_time(&nexttry); |
384 |
|
|
srv_get_envelope(evp); |
385 |
|
|
srv_end(); |
386 |
|
|
|
387 |
|
|
evp->flags |= flags; |
388 |
|
|
evp->nexttry = nexttry; |
389 |
|
|
|
390 |
|
|
from = evp->id + 1; |
391 |
|
|
found++; |
392 |
|
|
return (1); |
393 |
|
|
} |
394 |
|
|
|
395 |
|
|
static int |
396 |
|
|
srv_iter_evpids(uint32_t msgid, uint64_t *evpid, int *offset) |
397 |
|
|
{ |
398 |
|
|
static uint64_t *evpids = NULL, *tmp; |
399 |
|
|
static int n, tmpalloc, alloc = 0; |
400 |
|
|
struct envelope evp; |
401 |
|
|
|
402 |
|
|
if (*offset == 0) { |
403 |
|
|
n = 0; |
404 |
|
|
while (srv_iter_envelopes(msgid, &evp)) { |
405 |
|
|
if (n == alloc) { |
406 |
|
|
tmpalloc = alloc ? (alloc * 2) : 128; |
407 |
|
|
tmp = recallocarray(evpids, alloc, tmpalloc, |
408 |
|
|
sizeof(*evpids)); |
409 |
|
|
if (tmp == NULL) |
410 |
|
|
err(1, "recallocarray"); |
411 |
|
|
evpids = tmp; |
412 |
|
|
alloc = tmpalloc; |
413 |
|
|
} |
414 |
|
|
evpids[n++] = evp.id; |
415 |
|
|
} |
416 |
|
|
} |
417 |
|
|
|
418 |
|
|
if (*offset >= n) |
419 |
|
|
return (0); |
420 |
|
|
*evpid = evpids[*offset]; |
421 |
|
|
*offset += 1; |
422 |
|
|
return (1); |
423 |
|
|
} |
424 |
|
|
|
425 |
|
|
static void |
426 |
|
|
srv_foreach_envelope(struct parameter *argv, int ctl, size_t *total, size_t *ok) |
427 |
|
|
{ |
428 |
|
|
uint32_t msgid; |
429 |
|
|
uint64_t evpid; |
430 |
|
|
int i; |
431 |
|
|
|
432 |
|
|
*total = 0; |
433 |
|
|
*ok = 0; |
434 |
|
|
|
435 |
|
|
if (argv == NULL) { |
436 |
|
|
while (srv_iter_messages(&msgid)) { |
437 |
|
|
i = 0; |
438 |
|
|
while (srv_iter_evpids(msgid, &evpid, &i)) { |
439 |
|
|
*total += 1; |
440 |
|
|
srv_send(ctl, &evpid, sizeof(evpid)); |
441 |
|
|
if (srv_check_result(0) == 0) |
442 |
|
|
*ok += 1; |
443 |
|
|
} |
444 |
|
|
} |
445 |
|
|
} else if (argv->type == P_MSGID) { |
446 |
|
|
i = 0; |
447 |
|
|
while (srv_iter_evpids(argv->u.u_msgid, &evpid, &i)) { |
448 |
|
|
srv_send(ctl, &evpid, sizeof(evpid)); |
449 |
|
|
if (srv_check_result(0) == 0) |
450 |
|
|
*ok += 1; |
451 |
|
|
} |
452 |
|
|
} else { |
453 |
|
|
*total += 1; |
454 |
|
|
srv_send(ctl, &argv->u.u_evpid, sizeof(evpid)); |
455 |
|
|
if (srv_check_result(0) == 0) |
456 |
|
|
*ok += 1; |
457 |
|
|
} |
458 |
|
|
} |
459 |
|
|
|
460 |
|
|
static void |
461 |
|
|
srv_show_cmd(int cmd, const void *data, size_t len) |
462 |
|
|
{ |
463 |
|
|
int done = 0; |
464 |
|
|
|
465 |
|
|
srv_send(cmd, data, len); |
466 |
|
|
|
467 |
|
|
do { |
468 |
|
|
srv_recv(cmd); |
469 |
|
|
if (rlen) { |
470 |
|
|
printf("%s\n", rdata); |
471 |
|
|
srv_read(NULL, rlen); |
472 |
|
|
} |
473 |
|
|
else |
474 |
|
|
done = 1; |
475 |
|
|
srv_end(); |
476 |
|
|
} while (!done); |
477 |
|
|
} |
478 |
|
|
|
479 |
|
|
static int |
480 |
|
|
do_log_brief(int argc, struct parameter *argv) |
481 |
|
|
{ |
482 |
|
|
int v = 0; |
483 |
|
|
|
484 |
|
|
srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v)); |
485 |
|
|
return srv_check_result(1); |
486 |
|
|
} |
487 |
|
|
|
488 |
|
|
static int |
489 |
|
|
do_log_verbose(int argc, struct parameter *argv) |
490 |
|
|
{ |
491 |
|
|
int v = TRACE_DEBUG; |
492 |
|
|
|
493 |
|
|
srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v)); |
494 |
|
|
return srv_check_result(1); |
495 |
|
|
} |
496 |
|
|
|
497 |
|
|
static int |
498 |
|
|
do_monitor(int argc, struct parameter *argv) |
499 |
|
|
{ |
500 |
|
|
struct stat_digest last, digest; |
501 |
|
|
size_t count; |
502 |
|
|
|
503 |
|
|
memset(&last, 0, sizeof(last)); |
504 |
|
|
count = 0; |
505 |
|
|
|
506 |
|
|
while (1) { |
507 |
|
|
srv_send(IMSG_CTL_GET_DIGEST, NULL, 0); |
508 |
|
|
srv_recv(IMSG_CTL_GET_DIGEST); |
509 |
|
|
srv_read(&digest, sizeof(digest)); |
510 |
|
|
srv_end(); |
511 |
|
|
|
512 |
|
|
if (count % 25 == 0) { |
513 |
|
|
if (count != 0) |
514 |
|
|
printf("\n"); |
515 |
|
|
printf("--- client --- " |
516 |
|
|
"-- envelope -- " |
517 |
|
|
"---- relay/delivery --- " |
518 |
|
|
"------- misc -------\n" |
519 |
|
|
"curr conn disc " |
520 |
|
|
"curr enq deq " |
521 |
|
|
"ok tmpfail prmfail loop " |
522 |
|
|
"expire remove bounce\n"); |
523 |
|
|
} |
524 |
|
|
printf("%4zu %4zu %4zu " |
525 |
|
|
"%4zu %4zu %4zu " |
526 |
|
|
"%4zu %4zu %4zu %4zu " |
527 |
|
|
"%4zu %4zu %4zu\n", |
528 |
|
|
digest.clt_connect - digest.clt_disconnect, |
529 |
|
|
digest.clt_connect - last.clt_connect, |
530 |
|
|
digest.clt_disconnect - last.clt_disconnect, |
531 |
|
|
|
532 |
|
|
digest.evp_enqueued - digest.evp_dequeued, |
533 |
|
|
digest.evp_enqueued - last.evp_enqueued, |
534 |
|
|
digest.evp_dequeued - last.evp_dequeued, |
535 |
|
|
|
536 |
|
|
digest.dlv_ok - last.dlv_ok, |
537 |
|
|
digest.dlv_tempfail - last.dlv_tempfail, |
538 |
|
|
digest.dlv_permfail - last.dlv_permfail, |
539 |
|
|
digest.dlv_loop - last.dlv_loop, |
540 |
|
|
|
541 |
|
|
digest.evp_expired - last.evp_expired, |
542 |
|
|
digest.evp_removed - last.evp_removed, |
543 |
|
|
digest.evp_bounce - last.evp_bounce); |
544 |
|
|
|
545 |
|
|
last = digest; |
546 |
|
|
count++; |
547 |
|
|
sleep(1); |
548 |
|
|
} |
549 |
|
|
|
550 |
|
|
return (0); |
551 |
|
|
} |
552 |
|
|
|
553 |
|
|
static int |
554 |
|
|
do_pause_envelope(int argc, struct parameter *argv) |
555 |
|
|
{ |
556 |
|
|
size_t total, ok; |
557 |
|
|
|
558 |
|
|
srv_foreach_envelope(argv, IMSG_CTL_PAUSE_EVP, &total, &ok); |
559 |
|
|
printf("%zu envelope%s paused\n", ok, (ok > 1) ? "s" : ""); |
560 |
|
|
|
561 |
|
|
return (0); |
562 |
|
|
} |
563 |
|
|
|
564 |
|
|
static int |
565 |
|
|
do_pause_mda(int argc, struct parameter *argv) |
566 |
|
|
{ |
567 |
|
|
srv_send(IMSG_CTL_PAUSE_MDA, NULL, 0); |
568 |
|
|
return srv_check_result(1); |
569 |
|
|
} |
570 |
|
|
|
571 |
|
|
static int |
572 |
|
|
do_pause_mta(int argc, struct parameter *argv) |
573 |
|
|
{ |
574 |
|
|
srv_send(IMSG_CTL_PAUSE_MTA, NULL, 0); |
575 |
|
|
return srv_check_result(1); |
576 |
|
|
} |
577 |
|
|
|
578 |
|
|
static int |
579 |
|
|
do_pause_smtp(int argc, struct parameter *argv) |
580 |
|
|
{ |
581 |
|
|
srv_send(IMSG_CTL_PAUSE_SMTP, NULL, 0); |
582 |
|
|
return srv_check_result(1); |
583 |
|
|
} |
584 |
|
|
|
585 |
|
|
static int |
586 |
|
|
do_profile(int argc, struct parameter *argv) |
587 |
|
|
{ |
588 |
|
|
int v; |
589 |
|
|
|
590 |
|
|
v = str_to_profile(argv[0].u.u_str); |
591 |
|
|
|
592 |
|
|
srv_send(IMSG_CTL_PROFILE_ENABLE, &v, sizeof(v)); |
593 |
|
|
return srv_check_result(1); |
594 |
|
|
} |
595 |
|
|
|
596 |
|
|
static int |
597 |
|
|
do_remove(int argc, struct parameter *argv) |
598 |
|
|
{ |
599 |
|
|
size_t total, ok; |
600 |
|
|
|
601 |
|
|
srv_foreach_envelope(argv, IMSG_CTL_REMOVE, &total, &ok); |
602 |
|
|
printf("%zu envelope%s removed\n", ok, (ok > 1) ? "s" : ""); |
603 |
|
|
|
604 |
|
|
return (0); |
605 |
|
|
} |
606 |
|
|
|
607 |
|
|
static int |
608 |
|
|
do_resume_envelope(int argc, struct parameter *argv) |
609 |
|
|
{ |
610 |
|
|
size_t total, ok; |
611 |
|
|
|
612 |
|
|
srv_foreach_envelope(argv, IMSG_CTL_RESUME_EVP, &total, &ok); |
613 |
|
|
printf("%zu envelope%s resumed\n", ok, (ok > 1) ? "s" : ""); |
614 |
|
|
|
615 |
|
|
return (0); |
616 |
|
|
} |
617 |
|
|
|
618 |
|
|
static int |
619 |
|
|
do_resume_mda(int argc, struct parameter *argv) |
620 |
|
|
{ |
621 |
|
|
srv_send(IMSG_CTL_RESUME_MDA, NULL, 0); |
622 |
|
|
return srv_check_result(1); |
623 |
|
|
} |
624 |
|
|
|
625 |
|
|
static int |
626 |
|
|
do_resume_mta(int argc, struct parameter *argv) |
627 |
|
|
{ |
628 |
|
|
srv_send(IMSG_CTL_RESUME_MTA, NULL, 0); |
629 |
|
|
return srv_check_result(1); |
630 |
|
|
} |
631 |
|
|
|
632 |
|
|
static int |
633 |
|
|
do_resume_route(int argc, struct parameter *argv) |
634 |
|
|
{ |
635 |
|
|
uint64_t v; |
636 |
|
|
|
637 |
|
|
if (argc == 0) |
638 |
|
|
v = 0; |
639 |
|
|
else |
640 |
|
|
v = argv[0].u.u_routeid; |
641 |
|
|
|
642 |
|
|
srv_send(IMSG_CTL_RESUME_ROUTE, &v, sizeof(v)); |
643 |
|
|
return srv_check_result(1); |
644 |
|
|
} |
645 |
|
|
|
646 |
|
|
static int |
647 |
|
|
do_resume_smtp(int argc, struct parameter *argv) |
648 |
|
|
{ |
649 |
|
|
srv_send(IMSG_CTL_RESUME_SMTP, NULL, 0); |
650 |
|
|
return srv_check_result(1); |
651 |
|
|
} |
652 |
|
|
|
653 |
|
|
static int |
654 |
|
|
do_schedule(int argc, struct parameter *argv) |
655 |
|
|
{ |
656 |
|
|
size_t total, ok; |
657 |
|
|
|
658 |
|
|
srv_foreach_envelope(argv, IMSG_CTL_SCHEDULE, &total, &ok); |
659 |
|
|
printf("%zu envelope%s scheduled\n", ok, (ok > 1) ? "s" : ""); |
660 |
|
|
|
661 |
|
|
return (0); |
662 |
|
|
} |
663 |
|
|
|
664 |
|
|
static int |
665 |
|
|
do_show_envelope(int argc, struct parameter *argv) |
666 |
|
|
{ |
667 |
|
|
char buf[PATH_MAX]; |
668 |
|
|
|
669 |
|
|
if (!bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/%016" PRIx64, |
670 |
|
|
PATH_SPOOL, |
671 |
|
|
PATH_QUEUE, |
672 |
|
|
(evpid_to_msgid(argv[0].u.u_evpid) & 0xff000000) >> 24, |
673 |
|
|
evpid_to_msgid(argv[0].u.u_evpid), |
674 |
|
|
argv[0].u.u_evpid)) |
675 |
|
|
errx(1, "unable to retrieve envelope"); |
676 |
|
|
|
677 |
|
|
display(buf); |
678 |
|
|
|
679 |
|
|
return (0); |
680 |
|
|
} |
681 |
|
|
|
682 |
|
|
static int |
683 |
|
|
do_show_hoststats(int argc, struct parameter *argv) |
684 |
|
|
{ |
685 |
|
|
srv_show_cmd(IMSG_CTL_MTA_SHOW_HOSTSTATS, NULL, 0); |
686 |
|
|
|
687 |
|
|
return (0); |
688 |
|
|
} |
689 |
|
|
|
690 |
|
|
static int |
691 |
|
|
do_show_message(int argc, struct parameter *argv) |
692 |
|
|
{ |
693 |
|
|
char buf[PATH_MAX]; |
694 |
|
|
uint32_t msgid; |
695 |
|
|
|
696 |
|
|
if (argv[0].type == P_EVPID) |
697 |
|
|
msgid = evpid_to_msgid(argv[0].u.u_evpid); |
698 |
|
|
else |
699 |
|
|
msgid = argv[0].u.u_msgid; |
700 |
|
|
|
701 |
|
|
if (!bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/message", |
702 |
|
|
PATH_SPOOL, |
703 |
|
|
PATH_QUEUE, |
704 |
|
|
(msgid & 0xff000000) >> 24, |
705 |
|
|
msgid)) |
706 |
|
|
errx(1, "unable to retrieve message"); |
707 |
|
|
|
708 |
|
|
display(buf); |
709 |
|
|
|
710 |
|
|
return (0); |
711 |
|
|
} |
712 |
|
|
|
713 |
|
|
static int |
714 |
|
|
do_show_queue(int argc, struct parameter *argv) |
715 |
|
|
{ |
716 |
|
|
struct envelope evp; |
717 |
|
|
uint32_t msgid; |
718 |
|
|
FTS *fts; |
719 |
|
|
FTSENT *ftse; |
720 |
|
|
char *qpath[] = {"/queue", NULL}; |
721 |
|
|
char *tmp; |
722 |
|
|
uint64_t evpid; |
723 |
|
|
|
724 |
|
|
now = time(NULL); |
725 |
|
|
|
726 |
|
|
if (!srv_connect()) { |
727 |
|
|
log_init(1, LOG_MAIL); |
728 |
|
|
queue_init("fs", 0); |
729 |
|
|
if (chroot(PATH_SPOOL) == -1 || chdir("/") == -1) |
730 |
|
|
err(1, "%s", PATH_SPOOL); |
731 |
|
|
fts = fts_open(qpath, FTS_PHYSICAL|FTS_NOCHDIR, NULL); |
732 |
|
|
if (fts == NULL) |
733 |
|
|
err(1, "%s/queue", PATH_SPOOL); |
734 |
|
|
|
735 |
|
|
while ((ftse = fts_read(fts)) != NULL) { |
736 |
|
|
switch (ftse->fts_info) { |
737 |
|
|
case FTS_DP: |
738 |
|
|
case FTS_DNR: |
739 |
|
|
break; |
740 |
|
|
case FTS_F: |
741 |
|
|
tmp = NULL; |
742 |
|
|
evpid = strtoull(ftse->fts_name, &tmp, 16); |
743 |
|
|
if (tmp && *tmp != '\0') |
744 |
|
|
break; |
745 |
|
|
show_offline_envelope(evpid); |
746 |
|
|
} |
747 |
|
|
} |
748 |
|
|
|
749 |
|
|
fts_close(fts); |
750 |
|
|
return (0); |
751 |
|
|
} |
752 |
|
|
|
753 |
|
|
if (argc == 0) { |
754 |
|
|
msgid = 0; |
755 |
|
|
while (srv_iter_messages(&msgid)) |
756 |
|
|
while (srv_iter_envelopes(msgid, &evp)) |
757 |
|
|
show_queue_envelope(&evp, 1); |
758 |
|
|
} else if (argv[0].type == P_MSGID) { |
759 |
|
|
while (srv_iter_envelopes(argv[0].u.u_msgid, &evp)) |
760 |
|
|
show_queue_envelope(&evp, 1); |
761 |
|
|
} |
762 |
|
|
|
763 |
|
|
return (0); |
764 |
|
|
} |
765 |
|
|
|
766 |
|
|
static int |
767 |
|
|
do_show_hosts(int argc, struct parameter *argv) |
768 |
|
|
{ |
769 |
|
|
srv_show_cmd(IMSG_CTL_MTA_SHOW_HOSTS, NULL, 0); |
770 |
|
|
|
771 |
|
|
return (0); |
772 |
|
|
} |
773 |
|
|
|
774 |
|
|
static int |
775 |
|
|
do_show_relays(int argc, struct parameter *argv) |
776 |
|
|
{ |
777 |
|
|
srv_show_cmd(IMSG_CTL_MTA_SHOW_RELAYS, NULL, 0); |
778 |
|
|
|
779 |
|
|
return (0); |
780 |
|
|
} |
781 |
|
|
|
782 |
|
|
static int |
783 |
|
|
do_show_routes(int argc, struct parameter *argv) |
784 |
|
|
{ |
785 |
|
|
srv_show_cmd(IMSG_CTL_MTA_SHOW_ROUTES, NULL, 0); |
786 |
|
|
|
787 |
|
|
return (0); |
788 |
|
|
} |
789 |
|
|
|
790 |
|
|
static int |
791 |
|
|
do_show_stats(int argc, struct parameter *argv) |
792 |
|
|
{ |
793 |
|
|
struct stat_kv kv; |
794 |
|
|
time_t duration; |
795 |
|
|
|
796 |
|
|
memset(&kv, 0, sizeof kv); |
797 |
|
|
|
798 |
|
|
while (1) { |
799 |
|
|
srv_send(IMSG_CTL_GET_STATS, &kv, sizeof kv); |
800 |
|
|
srv_recv(IMSG_CTL_GET_STATS); |
801 |
|
|
srv_read(&kv, sizeof(kv)); |
802 |
|
|
srv_end(); |
803 |
|
|
|
804 |
|
|
if (kv.iter == NULL) |
805 |
|
|
break; |
806 |
|
|
|
807 |
|
|
if (strcmp(kv.key, "uptime") == 0) { |
808 |
|
|
duration = time(NULL) - kv.val.u.counter; |
809 |
|
|
printf("uptime=%lld\n", (long long)duration); |
810 |
|
|
printf("uptime.human=%s\n", |
811 |
|
|
duration_to_text(duration)); |
812 |
|
|
} |
813 |
|
|
else { |
814 |
|
|
switch (kv.val.type) { |
815 |
|
|
case STAT_COUNTER: |
816 |
|
|
printf("%s=%zd\n", |
817 |
|
|
kv.key, kv.val.u.counter); |
818 |
|
|
break; |
819 |
|
|
case STAT_TIMESTAMP: |
820 |
|
|
printf("%s=%" PRId64 "\n", |
821 |
|
|
kv.key, (int64_t)kv.val.u.timestamp); |
822 |
|
|
break; |
823 |
|
|
case STAT_TIMEVAL: |
824 |
|
|
printf("%s=%lld.%lld\n", |
825 |
|
|
kv.key, (long long)kv.val.u.tv.tv_sec, |
826 |
|
|
(long long)kv.val.u.tv.tv_usec); |
827 |
|
|
break; |
828 |
|
|
case STAT_TIMESPEC: |
829 |
|
|
printf("%s=%lld.%06ld\n", |
830 |
|
|
kv.key, |
831 |
|
|
(long long)kv.val.u.ts.tv_sec * 1000000 + |
832 |
|
|
kv.val.u.ts.tv_nsec / 1000000, |
833 |
|
|
kv.val.u.ts.tv_nsec % 1000000); |
834 |
|
|
break; |
835 |
|
|
} |
836 |
|
|
} |
837 |
|
|
} |
838 |
|
|
|
839 |
|
|
return (0); |
840 |
|
|
} |
841 |
|
|
|
842 |
|
|
static int |
843 |
|
|
do_show_status(int argc, struct parameter *argv) |
844 |
|
|
{ |
845 |
|
|
uint32_t sc_flags; |
846 |
|
|
|
847 |
|
|
srv_send(IMSG_CTL_SHOW_STATUS, NULL, 0); |
848 |
|
|
srv_recv(IMSG_CTL_SHOW_STATUS); |
849 |
|
|
srv_read(&sc_flags, sizeof(sc_flags)); |
850 |
|
|
srv_end(); |
851 |
|
|
printf("MDA %s\n", |
852 |
|
|
(sc_flags & SMTPD_MDA_PAUSED) ? "paused" : "running"); |
853 |
|
|
printf("MTA %s\n", |
854 |
|
|
(sc_flags & SMTPD_MTA_PAUSED) ? "paused" : "running"); |
855 |
|
|
printf("SMTP %s\n", |
856 |
|
|
(sc_flags & SMTPD_SMTP_PAUSED) ? "paused" : "running"); |
857 |
|
|
return (0); |
858 |
|
|
} |
859 |
|
|
|
860 |
|
|
static int |
861 |
|
|
do_trace(int argc, struct parameter *argv) |
862 |
|
|
{ |
863 |
|
|
int v; |
864 |
|
|
|
865 |
|
|
v = str_to_trace(argv[0].u.u_str); |
866 |
|
|
|
867 |
|
|
srv_send(IMSG_CTL_TRACE_ENABLE, &v, sizeof(v)); |
868 |
|
|
return srv_check_result(1); |
869 |
|
|
} |
870 |
|
|
|
871 |
|
|
static int |
872 |
|
|
do_unprofile(int argc, struct parameter *argv) |
873 |
|
|
{ |
874 |
|
|
int v; |
875 |
|
|
|
876 |
|
|
v = str_to_profile(argv[0].u.u_str); |
877 |
|
|
|
878 |
|
|
srv_send(IMSG_CTL_PROFILE_DISABLE, &v, sizeof(v)); |
879 |
|
|
return srv_check_result(1); |
880 |
|
|
} |
881 |
|
|
|
882 |
|
|
static int |
883 |
|
|
do_untrace(int argc, struct parameter *argv) |
884 |
|
|
{ |
885 |
|
|
int v; |
886 |
|
|
|
887 |
|
|
v = str_to_trace(argv[0].u.u_str); |
888 |
|
|
|
889 |
|
|
srv_send(IMSG_CTL_TRACE_DISABLE, &v, sizeof(v)); |
890 |
|
|
return srv_check_result(1); |
891 |
|
|
} |
892 |
|
|
|
893 |
|
|
static int |
894 |
|
|
do_update_table(int argc, struct parameter *argv) |
895 |
|
|
{ |
896 |
|
|
const char *name = argv[0].u.u_str; |
897 |
|
|
|
898 |
|
|
srv_send(IMSG_CTL_UPDATE_TABLE, name, strlen(name) + 1); |
899 |
|
|
return srv_check_result(1); |
900 |
|
|
} |
901 |
|
|
|
902 |
|
|
static int |
903 |
|
|
do_encrypt(int argc, struct parameter *argv) |
904 |
|
|
{ |
905 |
|
|
const char *p = NULL; |
906 |
|
|
|
907 |
|
|
if (argv) |
908 |
|
|
p = argv[0].u.u_str; |
909 |
|
|
execl(PATH_ENCRYPT, "encrypt", p, (char *)NULL); |
910 |
|
|
errx(1, "execl"); |
911 |
|
|
} |
912 |
|
|
|
913 |
|
|
static int |
914 |
|
|
do_block_mta(int argc, struct parameter *argv) |
915 |
|
|
{ |
916 |
|
|
struct ibuf *m; |
917 |
|
|
|
918 |
|
|
if (ibuf == NULL && !srv_connect()) |
919 |
|
|
errx(1, "smtpd doesn't seem to be running"); |
920 |
|
|
m = imsg_create(ibuf, IMSG_CTL_MTA_BLOCK, IMSG_VERSION, 0, |
921 |
|
|
sizeof(argv[0].u.u_ss) + strlen(argv[1].u.u_str) + 1); |
922 |
|
|
if (imsg_add(m, &argv[0].u.u_ss, sizeof(argv[0].u.u_ss)) == -1) |
923 |
|
|
errx(1, "imsg_add"); |
924 |
|
|
if (imsg_add(m, argv[1].u.u_str, strlen(argv[1].u.u_str) + 1) == -1) |
925 |
|
|
errx(1, "imsg_add"); |
926 |
|
|
imsg_close(ibuf, m); |
927 |
|
|
|
928 |
|
|
return srv_check_result(1); |
929 |
|
|
} |
930 |
|
|
|
931 |
|
|
static int |
932 |
|
|
do_unblock_mta(int argc, struct parameter *argv) |
933 |
|
|
{ |
934 |
|
|
struct ibuf *m; |
935 |
|
|
|
936 |
|
|
if (ibuf == NULL && !srv_connect()) |
937 |
|
|
errx(1, "smtpd doesn't seem to be running"); |
938 |
|
|
|
939 |
|
|
m = imsg_create(ibuf, IMSG_CTL_MTA_UNBLOCK, IMSG_VERSION, 0, |
940 |
|
|
sizeof(argv[0].u.u_ss) + strlen(argv[1].u.u_str) + 1); |
941 |
|
|
if (imsg_add(m, &argv[0].u.u_ss, sizeof(argv[0].u.u_ss)) == -1) |
942 |
|
|
errx(1, "imsg_add"); |
943 |
|
|
if (imsg_add(m, argv[1].u.u_str, strlen(argv[1].u.u_str) + 1) == -1) |
944 |
|
|
errx(1, "imsg_add"); |
945 |
|
|
imsg_close(ibuf, m); |
946 |
|
|
|
947 |
|
|
return srv_check_result(1); |
948 |
|
|
} |
949 |
|
|
|
950 |
|
|
static int |
951 |
|
|
do_show_mta_block(int argc, struct parameter *argv) |
952 |
|
|
{ |
953 |
|
|
srv_show_cmd(IMSG_CTL_MTA_SHOW_BLOCK, NULL, 0); |
954 |
|
|
|
955 |
|
|
return (0); |
956 |
|
|
} |
957 |
|
|
|
958 |
|
|
static int |
959 |
|
|
do_discover(int argc, struct parameter *argv) |
960 |
|
|
{ |
961 |
|
|
uint64_t evpid; |
962 |
|
|
uint32_t msgid; |
963 |
|
|
size_t n_evp; |
964 |
|
|
|
965 |
|
|
if (ibuf == NULL && !srv_connect()) |
966 |
|
|
errx(1, "smtpd doesn't seem to be running"); |
967 |
|
|
|
968 |
|
|
if (argv[0].type == P_EVPID) { |
969 |
|
|
evpid = argv[0].u.u_evpid; |
970 |
|
|
srv_send(IMSG_CTL_DISCOVER_EVPID, &evpid, sizeof evpid); |
971 |
|
|
srv_recv(IMSG_CTL_DISCOVER_EVPID); |
972 |
|
|
} else { |
973 |
|
|
msgid = argv[0].u.u_msgid; |
974 |
|
|
srv_send(IMSG_CTL_DISCOVER_MSGID, &msgid, sizeof msgid); |
975 |
|
|
srv_recv(IMSG_CTL_DISCOVER_MSGID); |
976 |
|
|
} |
977 |
|
|
|
978 |
|
|
if (rlen == 0) { |
979 |
|
|
srv_end(); |
980 |
|
|
return (0); |
981 |
|
|
} else { |
982 |
|
|
srv_read(&n_evp, sizeof n_evp); |
983 |
|
|
srv_end(); |
984 |
|
|
} |
985 |
|
|
|
986 |
|
|
printf("%zu envelope%s discovered\n", n_evp, (n_evp != 1) ? "s" : ""); |
987 |
|
|
return (0); |
988 |
|
|
} |
989 |
|
|
|
990 |
|
|
static int |
991 |
|
|
do_uncorrupt(int argc, struct parameter *argv) |
992 |
|
|
{ |
993 |
|
|
uint32_t msgid; |
994 |
|
|
int ret; |
995 |
|
|
|
996 |
|
|
if (ibuf == NULL && !srv_connect()) |
997 |
|
|
errx(1, "smtpd doesn't seem to be running"); |
998 |
|
|
|
999 |
|
|
msgid = argv[0].u.u_msgid; |
1000 |
|
|
srv_send(IMSG_CTL_UNCORRUPT_MSGID, &msgid, sizeof msgid); |
1001 |
|
|
srv_recv(IMSG_CTL_UNCORRUPT_MSGID); |
1002 |
|
|
|
1003 |
|
|
if (rlen == 0) { |
1004 |
|
|
srv_end(); |
1005 |
|
|
return (0); |
1006 |
|
|
} else { |
1007 |
|
|
srv_read(&ret, sizeof ret); |
1008 |
|
|
srv_end(); |
1009 |
|
|
} |
1010 |
|
|
|
1011 |
|
|
printf("command %s\n", ret ? "succeeded" : "failed"); |
1012 |
|
|
return (0); |
1013 |
|
|
} |
1014 |
|
|
|
1015 |
|
|
int |
1016 |
|
|
main(int argc, char **argv) |
1017 |
|
|
{ |
1018 |
|
|
gid_t gid; |
1019 |
|
|
char *argv_mailq[] = { "show", "queue", NULL }; |
1020 |
|
|
|
1021 |
|
|
sendmail_compat(argc, argv); |
1022 |
|
|
if (geteuid()) |
1023 |
|
|
errx(1, "need root privileges"); |
1024 |
|
|
|
1025 |
|
|
gid = getgid(); |
1026 |
|
|
if (setresgid(gid, gid, gid) == -1) |
1027 |
|
|
err(1, "setresgid"); |
1028 |
|
|
|
1029 |
|
|
cmd_install("discover <evpid>", do_discover); |
1030 |
|
|
cmd_install("discover <msgid>", do_discover); |
1031 |
|
|
cmd_install("encrypt", do_encrypt); |
1032 |
|
|
cmd_install("encrypt <str>", do_encrypt); |
1033 |
|
|
cmd_install("pause mta from <addr> for <str>", do_block_mta); |
1034 |
|
|
cmd_install("resume mta from <addr> for <str>", do_unblock_mta); |
1035 |
|
|
cmd_install("show mta paused", do_show_mta_block); |
1036 |
|
|
cmd_install("log brief", do_log_brief); |
1037 |
|
|
cmd_install("log verbose", do_log_verbose); |
1038 |
|
|
cmd_install("monitor", do_monitor); |
1039 |
|
|
cmd_install("pause envelope <evpid>", do_pause_envelope); |
1040 |
|
|
cmd_install("pause envelope <msgid>", do_pause_envelope); |
1041 |
|
|
cmd_install("pause envelope all", do_pause_envelope); |
1042 |
|
|
cmd_install("pause mda", do_pause_mda); |
1043 |
|
|
cmd_install("pause mta", do_pause_mta); |
1044 |
|
|
cmd_install("pause smtp", do_pause_smtp); |
1045 |
|
|
cmd_install("profile <str>", do_profile); |
1046 |
|
|
cmd_install("remove <evpid>", do_remove); |
1047 |
|
|
cmd_install("remove <msgid>", do_remove); |
1048 |
|
|
cmd_install("remove all", do_remove); |
1049 |
|
|
cmd_install("resume envelope <evpid>", do_resume_envelope); |
1050 |
|
|
cmd_install("resume envelope <msgid>", do_resume_envelope); |
1051 |
|
|
cmd_install("resume envelope all", do_resume_envelope); |
1052 |
|
|
cmd_install("resume mda", do_resume_mda); |
1053 |
|
|
cmd_install("resume mta", do_resume_mta); |
1054 |
|
|
cmd_install("resume route <routeid>", do_resume_route); |
1055 |
|
|
cmd_install("resume smtp", do_resume_smtp); |
1056 |
|
|
cmd_install("schedule <msgid>", do_schedule); |
1057 |
|
|
cmd_install("schedule <evpid>", do_schedule); |
1058 |
|
|
cmd_install("schedule all", do_schedule); |
1059 |
|
|
cmd_install("show envelope <evpid>", do_show_envelope); |
1060 |
|
|
cmd_install("show hoststats", do_show_hoststats); |
1061 |
|
|
cmd_install("show message <msgid>", do_show_message); |
1062 |
|
|
cmd_install("show message <evpid>", do_show_message); |
1063 |
|
|
cmd_install("show queue", do_show_queue); |
1064 |
|
|
cmd_install("show queue <msgid>", do_show_queue); |
1065 |
|
|
cmd_install("show hosts", do_show_hosts); |
1066 |
|
|
cmd_install("show relays", do_show_relays); |
1067 |
|
|
cmd_install("show routes", do_show_routes); |
1068 |
|
|
cmd_install("show stats", do_show_stats); |
1069 |
|
|
cmd_install("show status", do_show_status); |
1070 |
|
|
cmd_install("trace <str>", do_trace); |
1071 |
|
|
cmd_install("uncorrupt <msgid>", do_uncorrupt); |
1072 |
|
|
cmd_install("unprofile <str>", do_unprofile); |
1073 |
|
|
cmd_install("untrace <str>", do_untrace); |
1074 |
|
|
cmd_install("update table <str>", do_update_table); |
1075 |
|
|
|
1076 |
|
|
if (strcmp(__progname, "mailq") == 0) |
1077 |
|
|
return cmd_run(2, argv_mailq); |
1078 |
|
|
if (strcmp(__progname, "smtpctl") == 0) |
1079 |
|
|
return cmd_run(argc - 1, argv + 1); |
1080 |
|
|
|
1081 |
|
|
errx(1, "unsupported mode"); |
1082 |
|
|
return (0); |
1083 |
|
|
} |
1084 |
|
|
|
1085 |
|
|
void |
1086 |
|
|
sendmail_compat(int argc, char **argv) |
1087 |
|
|
{ |
1088 |
|
|
FILE *offlinefp = NULL; |
1089 |
|
|
gid_t gid; |
1090 |
|
|
int i; |
1091 |
|
|
|
1092 |
|
|
if (strcmp(__progname, "sendmail") == 0 || |
1093 |
|
|
strcmp(__progname, "send-mail") == 0) { |
1094 |
|
|
/* |
1095 |
|
|
* determine whether we are called with flags |
1096 |
|
|
* that should invoke makemap/newaliases. |
1097 |
|
|
*/ |
1098 |
|
|
for (i = 1; i < argc; i++) |
1099 |
|
|
if (strncmp(argv[i], "-bi", 3) == 0) |
1100 |
|
|
exit(makemap(P_NEWALIASES, argc, argv)); |
1101 |
|
|
|
1102 |
|
|
if (!srv_connect()) |
1103 |
|
|
offlinefp = offline_file(); |
1104 |
|
|
|
1105 |
|
|
gid = getgid(); |
1106 |
|
|
if (setresgid(gid, gid, gid) == -1) |
1107 |
|
|
err(1, "setresgid"); |
1108 |
|
|
|
1109 |
|
|
/* we'll reduce further down the road */ |
1110 |
|
|
if (pledge("stdio rpath wpath cpath tmppath flock " |
1111 |
|
|
"dns getpw recvfd", NULL) == -1) |
1112 |
|
|
err(1, "pledge"); |
1113 |
|
|
|
1114 |
|
|
sendmail = 1; |
1115 |
|
|
exit(enqueue(argc, argv, offlinefp)); |
1116 |
|
|
} else if (strcmp(__progname, "makemap") == 0) |
1117 |
|
|
exit(makemap(P_MAKEMAP, argc, argv)); |
1118 |
|
|
else if (strcmp(__progname, "newaliases") == 0) |
1119 |
|
|
exit(makemap(P_NEWALIASES, argc, argv)); |
1120 |
|
|
} |
1121 |
|
|
|
1122 |
|
|
static void |
1123 |
|
|
show_queue_envelope(struct envelope *e, int online) |
1124 |
|
|
{ |
1125 |
|
|
const char *src = "?", *agent = "?"; |
1126 |
|
|
char status[128], runstate[128]; |
1127 |
|
|
|
1128 |
|
|
status[0] = '\0'; |
1129 |
|
|
|
1130 |
|
|
getflag(&e->flags, EF_BOUNCE, "bounce", status, sizeof(status)); |
1131 |
|
|
getflag(&e->flags, EF_AUTHENTICATED, "auth", status, sizeof(status)); |
1132 |
|
|
getflag(&e->flags, EF_INTERNAL, "internal", status, sizeof(status)); |
1133 |
|
|
getflag(&e->flags, EF_SUSPEND, "suspend", status, sizeof(status)); |
1134 |
|
|
getflag(&e->flags, EF_HOLD, "hold", status, sizeof(status)); |
1135 |
|
|
|
1136 |
|
|
if (online) { |
1137 |
|
|
if (e->flags & EF_PENDING) |
1138 |
|
|
(void)snprintf(runstate, sizeof runstate, "pending|%zd", |
1139 |
|
|
(ssize_t)(e->nexttry - now)); |
1140 |
|
|
else if (e->flags & EF_INFLIGHT) |
1141 |
|
|
(void)snprintf(runstate, sizeof runstate, |
1142 |
|
|
"inflight|%zd", (ssize_t)(now - e->lasttry)); |
1143 |
|
|
else |
1144 |
|
|
(void)snprintf(runstate, sizeof runstate, "invalid|"); |
1145 |
|
|
e->flags &= ~(EF_PENDING|EF_INFLIGHT); |
1146 |
|
|
} |
1147 |
|
|
else |
1148 |
|
|
(void)strlcpy(runstate, "offline|", sizeof runstate); |
1149 |
|
|
|
1150 |
|
|
if (e->flags) |
1151 |
|
|
errx(1, "%016" PRIx64 ": unexpected flags 0x%04x", e->id, |
1152 |
|
|
e->flags); |
1153 |
|
|
|
1154 |
|
|
if (status[0]) |
1155 |
|
|
status[strlen(status) - 1] = '\0'; |
1156 |
|
|
|
1157 |
|
|
if (e->type == D_MDA) |
1158 |
|
|
agent = "mda"; |
1159 |
|
|
else if (e->type == D_MTA) |
1160 |
|
|
agent = "mta"; |
1161 |
|
|
else if (e->type == D_BOUNCE) |
1162 |
|
|
agent = "bounce"; |
1163 |
|
|
|
1164 |
|
|
if (e->ss.ss_family == AF_LOCAL) |
1165 |
|
|
src = "local"; |
1166 |
|
|
else if (e->ss.ss_family == AF_INET) |
1167 |
|
|
src = "inet4"; |
1168 |
|
|
else if (e->ss.ss_family == AF_INET6) |
1169 |
|
|
src = "inet6"; |
1170 |
|
|
|
1171 |
|
|
printf("%016"PRIx64 |
1172 |
|
|
"|%s|%s|%s|%s@%s|%s@%s|%s@%s" |
1173 |
|
|
"|%zu|%zu|%zu|%zu|%s|%s\n", |
1174 |
|
|
|
1175 |
|
|
e->id, |
1176 |
|
|
|
1177 |
|
|
src, |
1178 |
|
|
agent, |
1179 |
|
|
status, |
1180 |
|
|
e->sender.user, e->sender.domain, |
1181 |
|
|
e->rcpt.user, e->rcpt.domain, |
1182 |
|
|
e->dest.user, e->dest.domain, |
1183 |
|
|
|
1184 |
|
|
(size_t) e->creation, |
1185 |
|
|
(size_t) (e->creation + e->expire), |
1186 |
|
|
(size_t) e->lasttry, |
1187 |
|
|
(size_t) e->retry, |
1188 |
|
|
runstate, |
1189 |
|
|
e->errorline); |
1190 |
|
|
} |
1191 |
|
|
|
1192 |
|
|
static void |
1193 |
|
|
getflag(uint *bitmap, int bit, char *bitstr, char *buf, size_t len) |
1194 |
|
|
{ |
1195 |
|
|
if (*bitmap & bit) { |
1196 |
|
|
*bitmap &= ~bit; |
1197 |
|
|
(void)strlcat(buf, bitstr, len); |
1198 |
|
|
(void)strlcat(buf, ",", len); |
1199 |
|
|
} |
1200 |
|
|
} |
1201 |
|
|
|
1202 |
|
|
static void |
1203 |
|
|
show_offline_envelope(uint64_t evpid) |
1204 |
|
|
{ |
1205 |
|
|
FILE *fp = NULL; |
1206 |
|
|
char pathname[PATH_MAX]; |
1207 |
|
|
size_t plen; |
1208 |
|
|
char *p; |
1209 |
|
|
size_t buflen; |
1210 |
|
|
char buffer[sizeof(struct envelope)]; |
1211 |
|
|
|
1212 |
|
|
struct envelope evp; |
1213 |
|
|
|
1214 |
|
|
if (!bsnprintf(pathname, sizeof pathname, |
1215 |
|
|
"/queue/%02x/%08x/%016"PRIx64, |
1216 |
|
|
(evpid_to_msgid(evpid) & 0xff000000) >> 24, |
1217 |
|
|
evpid_to_msgid(evpid), evpid)) |
1218 |
|
|
goto end; |
1219 |
|
|
fp = fopen(pathname, "r"); |
1220 |
|
|
if (fp == NULL) |
1221 |
|
|
goto end; |
1222 |
|
|
|
1223 |
|
|
buflen = fread(buffer, 1, sizeof (buffer) - 1, fp); |
1224 |
|
|
p = buffer; |
1225 |
|
|
plen = buflen; |
1226 |
|
|
buffer[buflen] = '\0'; |
1227 |
|
|
|
1228 |
|
|
if (is_encrypted_buffer(p)) { |
1229 |
|
|
warnx("offline encrypted queue is not supported yet"); |
1230 |
|
|
goto end; |
1231 |
|
|
} |
1232 |
|
|
|
1233 |
|
|
if (is_gzip_buffer(p)) { |
1234 |
|
|
warnx("offline compressed queue is not supported yet"); |
1235 |
|
|
goto end; |
1236 |
|
|
} |
1237 |
|
|
|
1238 |
|
|
if (!envelope_load_buffer(&evp, p, plen)) |
1239 |
|
|
goto end; |
1240 |
|
|
evp.id = evpid; |
1241 |
|
|
show_queue_envelope(&evp, 0); |
1242 |
|
|
|
1243 |
|
|
end: |
1244 |
|
|
if (fp) |
1245 |
|
|
fclose(fp); |
1246 |
|
|
} |
1247 |
|
|
|
1248 |
|
|
static void |
1249 |
|
|
display(const char *s) |
1250 |
|
|
{ |
1251 |
|
|
FILE *fp; |
1252 |
|
|
char *key; |
1253 |
|
|
int gzipped; |
1254 |
|
|
char *gzcat_argv0 = strrchr(PATH_GZCAT, '/') + 1; |
1255 |
|
|
|
1256 |
|
|
if ((fp = fopen(s, "r")) == NULL) |
1257 |
|
|
err(1, "fopen"); |
1258 |
|
|
|
1259 |
|
|
if (is_encrypted_fp(fp)) { |
1260 |
|
|
int i; |
1261 |
|
|
int fd; |
1262 |
|
|
FILE *ofp = NULL; |
1263 |
|
|
char sfn[] = "/tmp/smtpd.XXXXXXXXXX"; |
1264 |
|
|
|
1265 |
|
|
if ((fd = mkstemp(sfn)) == -1 || |
1266 |
|
|
(ofp = fdopen(fd, "w+")) == NULL) { |
1267 |
|
|
int saved_errno = errno; |
1268 |
|
|
if (fd != -1) { |
1269 |
|
|
unlink(sfn); |
1270 |
|
|
close(fd); |
1271 |
|
|
} |
1272 |
|
|
errc(1, saved_errno, "mkstemp"); |
1273 |
|
|
} |
1274 |
|
|
unlink(sfn); |
1275 |
|
|
|
1276 |
|
|
for (i = 0; i < 3; i++) { |
1277 |
|
|
key = getpass("key> "); |
1278 |
|
|
if (crypto_setup(key, strlen(key))) |
1279 |
|
|
break; |
1280 |
|
|
} |
1281 |
|
|
if (i == 3) |
1282 |
|
|
errx(1, "crypto-setup: invalid key"); |
1283 |
|
|
|
1284 |
|
|
if (!crypto_decrypt_file(fp, ofp)) { |
1285 |
|
|
printf("object is encrypted: %s\n", key); |
1286 |
|
|
exit(1); |
1287 |
|
|
} |
1288 |
|
|
|
1289 |
|
|
fclose(fp); |
1290 |
|
|
fp = ofp; |
1291 |
|
|
fseek(fp, 0, SEEK_SET); |
1292 |
|
|
} |
1293 |
|
|
gzipped = is_gzip_fp(fp); |
1294 |
|
|
|
1295 |
|
|
lseek(fileno(fp), 0, SEEK_SET); |
1296 |
|
|
(void)dup2(fileno(fp), STDIN_FILENO); |
1297 |
|
|
if (gzipped) |
1298 |
|
|
execl(PATH_GZCAT, gzcat_argv0, (char *)NULL); |
1299 |
|
|
else |
1300 |
|
|
execl(PATH_CAT, "cat", (char *)NULL); |
1301 |
|
|
err(1, "execl"); |
1302 |
|
|
} |
1303 |
|
|
|
1304 |
|
|
static int |
1305 |
|
|
str_to_trace(const char *str) |
1306 |
|
|
{ |
1307 |
|
|
if (!strcmp(str, "imsg")) |
1308 |
|
|
return TRACE_IMSG; |
1309 |
|
|
if (!strcmp(str, "io")) |
1310 |
|
|
return TRACE_IO; |
1311 |
|
|
if (!strcmp(str, "smtp")) |
1312 |
|
|
return TRACE_SMTP; |
1313 |
|
|
if (!strcmp(str, "filters")) |
1314 |
|
|
return TRACE_FILTERS; |
1315 |
|
|
if (!strcmp(str, "mta")) |
1316 |
|
|
return TRACE_MTA; |
1317 |
|
|
if (!strcmp(str, "bounce")) |
1318 |
|
|
return TRACE_BOUNCE; |
1319 |
|
|
if (!strcmp(str, "scheduler")) |
1320 |
|
|
return TRACE_SCHEDULER; |
1321 |
|
|
if (!strcmp(str, "lookup")) |
1322 |
|
|
return TRACE_LOOKUP; |
1323 |
|
|
if (!strcmp(str, "stat")) |
1324 |
|
|
return TRACE_STAT; |
1325 |
|
|
if (!strcmp(str, "rules")) |
1326 |
|
|
return TRACE_RULES; |
1327 |
|
|
if (!strcmp(str, "mproc")) |
1328 |
|
|
return TRACE_MPROC; |
1329 |
|
|
if (!strcmp(str, "expand")) |
1330 |
|
|
return TRACE_EXPAND; |
1331 |
|
|
if (!strcmp(str, "all")) |
1332 |
|
|
return ~TRACE_DEBUG; |
1333 |
|
|
errx(1, "invalid trace keyword: %s", str); |
1334 |
|
|
return (0); |
1335 |
|
|
} |
1336 |
|
|
|
1337 |
|
|
static int |
1338 |
|
|
str_to_profile(const char *str) |
1339 |
|
|
{ |
1340 |
|
|
if (!strcmp(str, "imsg")) |
1341 |
|
|
return PROFILE_IMSG; |
1342 |
|
|
if (!strcmp(str, "queue")) |
1343 |
|
|
return PROFILE_QUEUE; |
1344 |
|
|
errx(1, "invalid profile keyword: %s", str); |
1345 |
|
|
return (0); |
1346 |
|
|
} |
1347 |
|
|
|
1348 |
|
|
static int |
1349 |
|
|
is_gzip_buffer(const char *buffer) |
1350 |
|
|
{ |
1351 |
|
|
uint16_t magic; |
1352 |
|
|
|
1353 |
|
|
memcpy(&magic, buffer, sizeof magic); |
1354 |
|
|
#define GZIP_MAGIC 0x8b1f |
1355 |
|
|
return (magic == GZIP_MAGIC); |
1356 |
|
|
} |
1357 |
|
|
|
1358 |
|
|
static int |
1359 |
|
|
is_gzip_fp(FILE *fp) |
1360 |
|
|
{ |
1361 |
|
|
uint8_t magic[2]; |
1362 |
|
|
int ret = 0; |
1363 |
|
|
|
1364 |
|
|
if (fread(&magic, 1, sizeof magic, fp) != sizeof magic) |
1365 |
|
|
goto end; |
1366 |
|
|
|
1367 |
|
|
ret = is_gzip_buffer((const char *)&magic); |
1368 |
|
|
end: |
1369 |
|
|
fseek(fp, 0, SEEK_SET); |
1370 |
|
|
return ret; |
1371 |
|
|
} |
1372 |
|
|
|
1373 |
|
|
|
1374 |
|
|
/* XXX */ |
1375 |
|
|
/* |
1376 |
|
|
* queue supports transparent encryption. |
1377 |
|
|
* encrypted chunks are prefixed with an API version byte |
1378 |
|
|
* which we ensure is unambiguous with gzipped / plain |
1379 |
|
|
* objects. |
1380 |
|
|
*/ |
1381 |
|
|
|
1382 |
|
|
static int |
1383 |
|
|
is_encrypted_buffer(const char *buffer) |
1384 |
|
|
{ |
1385 |
|
|
uint8_t magic; |
1386 |
|
|
|
1387 |
|
|
magic = *buffer; |
1388 |
|
|
#define ENCRYPTION_MAGIC 0x1 |
1389 |
|
|
return (magic == ENCRYPTION_MAGIC); |
1390 |
|
|
} |
1391 |
|
|
|
1392 |
|
|
static int |
1393 |
|
|
is_encrypted_fp(FILE *fp) |
1394 |
|
|
{ |
1395 |
|
|
uint8_t magic; |
1396 |
|
|
int ret = 0; |
1397 |
|
|
|
1398 |
|
|
if (fread(&magic, 1, sizeof magic, fp) != sizeof magic) |
1399 |
|
|
goto end; |
1400 |
|
|
|
1401 |
|
|
ret = is_encrypted_buffer((const char *)&magic); |
1402 |
|
|
end: |
1403 |
|
|
fseek(fp, 0, SEEK_SET); |
1404 |
|
|
return ret; |
1405 |
|
|
} |