1 /* This file is part of gacopyz.
2 Copyright (C) 2006-2021 Sergey Poznyakoff
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <gacopyz_priv.h>
18
19 #define TRACE(ctx,cmd,size,buf) do { \
20 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_DEBUG)) { \
21 gacopyz_log(SMI_LOG_DEBUG, _("send header: size=%lu, cmd=%c"),\
22 size, cmd); \
23 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_PROTO)) \
24 gacopyz_logdump(SMI_LOG_PROTO, \
25 _("send data"), buf, size); \
26 else \
27 gacopyz_log(SMI_LOG_DEBUG, _("send data")); \
28 } \
29 } while (0)
30
31 int
gacopyz_init(gacopyz_conn_t * pconn,struct smfiDesc * desc)32 gacopyz_init(gacopyz_conn_t *pconn, struct smfiDesc *desc)
33 {
34 size_t len;
35 char *name;
36 gacopyz_conn_t conn;
37
38 name = desc->xxfi_name ? desc->xxfi_name : "Unknown";
39 len = strlen(name);
40 if (desc->xxfi_version != SMFI_VERSION) {
41 gacopyz_log(SMI_LOG_ERR,
42 _("smfi_register: %s: version mismatch; "
43 "application %d != implementation %d"),
44 name, desc->xxfi_version, SMFI_VERSION);
45 return MI_FAILURE;
46 }
47
48 conn = malloc(sizeof(*conn) + len + 1);
49
50 if (!conn) {
51 gacopyz_log(SMI_LOG_ERR,
52 "smfi_register: %s: %s", name,
53 strerror(errno));
54 return MI_FAILURE;
55 }
56
57 memset(conn, 0, sizeof(*conn));
58 conn->sd = -1;
59 conn->master_timeout.tv_usec = 0;
60 conn->master_timeout.tv_sec = 5;
61 conn->desc.ctx_timeout.tv_usec = 0;
62 conn->desc.ctx_timeout.tv_sec = GACOPYZ_TIMEOUT;
63 if (!conn->desc.logmask)
64 conn->desc.logmask = SMI_DEFAULT_LOG_MASK;
65 conn->desc = *desc;
66 conn->desc.xxfi_name = (char*)(conn + 1);
67 strcpy(conn->desc.xxfi_name, name);
68 *pconn = conn;
69 return MI_SUCCESS;
70 }
71
72 void
gacopyz_free(gacopyz_conn_t conn)73 gacopyz_free(gacopyz_conn_t conn)
74 {
75 if (conn) {
76 free(conn->pidtab);
77 free(conn);
78 }
79 }
80
81 static int
copy_part(const char * cstr,const char * p,char ** pbuf)82 copy_part(const char *cstr, const char *p, char **pbuf)
83 {
84 size_t len = p - cstr;
85 char *buf = malloc(len + 1);
86 if (!buf) {
87 free(buf);
88 return MI_FAILURE;
89 }
90 memcpy(buf, cstr, len);
91 buf[len] = 0;
92 *pbuf = buf;
93 return 0;
94 }
95
96
97 static int
parse_url(const char * proto,const char * cstr,char ** pport,char ** ppath)98 parse_url(const char *proto, const char *cstr, char **pport, char **ppath)
99 {
100 const char *p;
101
102 if (cstr[0] == '[') {
103 p = strchr(cstr + 1, ']');
104 if (p) {
105 if (copy_part(cstr + 1, p, ppath))
106 return MI_FAILURE;
107 if (*++p == ':') {
108 *pport = strdup(p + 1);
109 return *pport == NULL ? MI_FAILURE : 0;
110 } else if (p == NULL) {
111 *pport = NULL;
112 return 0;
113 }
114 free(*ppath);
115 /* Retry parsing as a usual address:port */
116 }
117 }
118
119 p = strchr(cstr, ':');
120
121 if (!p) {
122 *pport = NULL;
123 *ppath = strdup(cstr);
124 return *ppath == NULL ? MI_FAILURE : 0;
125 } else if (copy_part(cstr, p, ppath))
126 return MI_FAILURE;
127 else
128 cstr = p + 1;
129 *pport = strdup(cstr);
130 return *pport == NULL ? MI_FAILURE : 0;
131 }
132
133 int
gacopyz_parse_connection(const char * cstr,char ** pproto,char ** pport,char ** ppath)134 gacopyz_parse_connection(const char *cstr,
135 char **pproto, char **pport, char **ppath)
136 {
137 const char *p;
138 size_t len;
139
140 p = strchr(cstr, ':');
141 if (!p)
142 *pproto = NULL;
143 else {
144 char *proto;
145 if (copy_part(cstr, p, &proto))
146 return MI_FAILURE;
147 cstr = p + 1;
148 if (cstr[0] == '/' && cstr[1] == '/') {
149 int rc = parse_url(proto, cstr + 2, pport, ppath);
150 if (rc)
151 free(proto);
152 else
153 *pproto = proto;
154 return rc;
155 }
156 *pproto = proto;
157 }
158
159 p = strchr(cstr, '@');
160 if (!p)
161 *pport = NULL;
162 else if (copy_part(cstr, p, pport))
163 return MI_FAILURE;
164 else
165 cstr = p + 1;
166
167 len = strlen (cstr);
168 if (cstr[0] == '[' && cstr[len-1] == ']') {
169 if (copy_part(cstr + 1, cstr + len - 1, ppath)) {
170 free(*pproto);
171 free(*pport);
172 return MI_FAILURE;
173 }
174 } else
175 *ppath = strdup(cstr);
176 if (!*ppath) {
177 free(*pproto);
178 free(*pport);
179 free(*ppath);
180 return MI_FAILURE;
181 }
182 return MI_SUCCESS;
183 }
184
185
186 static int
parse_connection(gacopyz_conn_t conn,const char * cstr,char ** pproto,char ** pport,char ** ppath)187 parse_connection(gacopyz_conn_t conn,
188 const char *cstr,
189 char **pproto, char **pport, char **ppath)
190 {
191 int rc = gacopyz_parse_connection(cstr, pproto, pport, ppath);
192 if (rc && GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
193 gacopyz_log(SMI_LOG_ERR,
194 "parse_connection: %s", strerror(ENOMEM));
195 return rc;
196 }
197
198 static void
cleanup_unix_socket(gacopyz_conn_t conn,void * data)199 cleanup_unix_socket(gacopyz_conn_t conn, void *data)
200 {
201 if (unlink(data) && errno != ENOENT)
202 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
203 gacopyz_log(SMI_LOG_ERR,
204 _("%s: %s: cannot unlink: %s"),
205 conn->desc.xxfi_name, (char*)data,
206 strerror(errno));
207
208 free(data);
209 }
210
211 static int
do_connect(gacopyz_conn_t conn,const char * cstr,char * proto,char * port,char * path,int backlog,int rmsocket)212 do_connect(gacopyz_conn_t conn,
213 const char *cstr, char *proto, char *port, char *path,
214 int backlog, int rmsocket)
215 {
216 milter_sockaddr_t addr;
217 int socklen;
218 int fd, flags;
219 int yes = 1, rc;
220 mode_t old_mask;
221
222 if (!proto
223 || strcmp(proto, "unix") == 0 || strcmp(proto, "local") == 0) {
224 struct stat st;
225
226 if (port) {
227 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
228 gacopyz_log(SMI_LOG_ERR,
229 _("%s: invalid connection type: %s; "
230 "port is meaningless for UNIX sockets"),
231 conn->desc.xxfi_name, cstr);
232 return -1;
233 }
234
235 if (strlen(path) > sizeof addr.sunix.sun_path) {
236 errno = EINVAL;
237 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
238 gacopyz_log(SMI_LOG_ERR,
239 _("%s: %s: UNIX socket name too long"),
240 conn->desc.xxfi_name, path);
241 return -1;
242 }
243
244 addr.sa.sa_family = PF_UNIX;
245 socklen = sizeof(addr.sunix);
246 strcpy(addr.sunix.sun_path, path);
247
248 if (stat(path, &st)) {
249 if (errno == ENOENT) {
250 conn->cleanup = cleanup_unix_socket;
251 conn->cleanup_data = strdup(path);
252 } else {
253 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
254 gacopyz_log(SMI_LOG_ERR,
255 _("%s: %s: cannot stat socket: %s"),
256 conn->desc.xxfi_name, path,
257 strerror(errno));
258 return -1;
259 }
260 } else {
261 /* FIXME: Check permissions? */
262 if (!S_ISSOCK(st.st_mode)) {
263 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
264 gacopyz_log(SMI_LOG_ERR,
265 _("%s: %s: not a socket"),
266 conn->desc.xxfi_name,
267 path);
268 return -1;
269 }
270 if (rmsocket && unlink(path)) {
271 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
272 gacopyz_log(SMI_LOG_ERR,
273 _("%s: %s: cannot unlink: %s"),
274 conn->desc.xxfi_name, path,
275 strerror(errno));
276 return -1;
277 }
278 }
279
280 } else if (strcmp(proto, "inet") == 0) {
281 short pnum;
282 long num;
283 char *p;
284
285 addr.sa.sa_family = PF_INET;
286 socklen = sizeof(addr.sin);
287
288 if (!port) {
289 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
290 gacopyz_log(SMI_LOG_ERR,
291 _("%s: invalid connection type: %s; "
292 "missing port number"),
293 conn->desc.xxfi_name, cstr);
294 return -1;
295 }
296
297 num = pnum = strtol(port, &p, 0);
298 if (*p == 0) {
299 if (num != pnum) {
300 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
301 gacopyz_log(SMI_LOG_ERR,
302 _("%s: invalid connection type: "
303 "%s; bad port number"),
304 conn->desc.xxfi_name,
305 cstr);
306 return -1;
307 }
308 pnum = htons(pnum);
309 } else {
310 struct servent *sp = getservbyname(port, "tcp");
311 if (!sp) {
312 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
313 gacopyz_log(SMI_LOG_ERR,
314 _("%s: invalid connection type: "
315 "%s; unknown port name"),
316 conn->desc.xxfi_name,
317 cstr);
318 return -1;
319 }
320 pnum = sp->s_port;
321 }
322
323 if (!path)
324 addr.sin.sin_addr.s_addr = INADDR_ANY;
325 else {
326 struct hostent *hp = gethostbyname(path);
327 if (!hp) {
328 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
329 gacopyz_log(SMI_LOG_ERR,
330 _("%s: unknown host name %s"),
331 conn->desc.xxfi_name,
332 path);
333 return -1;
334 }
335 addr.sa.sa_family = hp->h_addrtype;
336 switch (hp->h_addrtype) {
337 case AF_INET:
338 memmove(&addr.sin.sin_addr, hp->h_addr, 4);
339 addr.sin.sin_port = pnum;
340 break;
341
342 default:
343 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
344 gacopyz_log(SMI_LOG_ERR,
345 _("%s: invalid connection type: "
346 "%s; unsupported address family"),
347 conn->desc.xxfi_name,
348 cstr);
349 return -1;
350 }
351 }
352 #ifdef GACOPYZ_IPV6
353 } else if (strcmp(proto, "inet6") == 0) {
354 struct addrinfo hints;
355 struct addrinfo *res;
356
357 memset(&hints, 0, sizeof(hints));
358 hints.ai_family = AF_INET6;
359 hints.ai_socktype = SOCK_STREAM;
360 if (!path)
361 hints.ai_flags |= AI_PASSIVE;
362 rc = getaddrinfo(path, port, &hints, &res);
363
364 switch (rc) {
365 case 0:
366 break;
367 case EAI_SYSTEM:
368 gacopyz_log(SMI_LOG_ERR,
369 _("%s:%s: cannot parse address: %s"),
370 path, port, strerror(errno));
371 return -1;
372
373 case EAI_BADFLAGS:
374 case EAI_SOCKTYPE:
375 gacopyz_log(SMI_LOG_ERR,
376 _("%s:%d: internal error converting %s:%s"),
377 __FILE__, __LINE__, path, port);
378 return -1;
379
380 case EAI_MEMORY:
381 gacopyz_log(SMI_LOG_ERR, "not enogh memory");
382 return -1;
383
384 default:
385 gacopyz_log(SMI_LOG_ERR,
386 "%s:%s: %s",
387 path, port, gai_strerror(rc));
388 return -1;
389 }
390
391 if (res->ai_addrlen > sizeof(addr)) {
392 gacopyz_log(SMI_LOG_ERR,
393 _("%s:%s: address length too big (%lu)"),
394 path, port,
395 (unsigned long) res->ai_addrlen);
396 freeaddrinfo(res);
397 return -1;
398 }
399
400 if (res->ai_next) {
401 char host[NI_MAXHOST], serv[NI_MAXSERV];
402 rc = getnameinfo(res->ai_addr, res->ai_addrlen,
403 host, sizeof host,
404 serv, sizeof serv,
405 NI_NUMERICHOST|NI_NUMERICSERV);
406
407 gacopyz_log(SMI_LOG_WARN,
408 _("%s:%s resolves to several addresses; "
409 "using %s:%s"),
410 path, port, host, serv);
411 }
412
413 memcpy(&addr, res->ai_addr, res->ai_addrlen);
414 freeaddrinfo(res);
415 #endif
416 } else {
417 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
418 gacopyz_log(SMI_LOG_ERR,
419 _("%s: unsupported protocol: %s"),
420 conn->desc.xxfi_name, proto);
421 return -1;
422 }
423
424 fd = socket(addr.sa.sa_family, SOCK_STREAM, 0);
425 if (fd == -1) {
426 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
427 gacopyz_log(SMI_LOG_ERR,
428 _("%s: unable to create new socket: %s"),
429 conn->desc.xxfi_name, strerror(errno));
430 return -1;
431 }
432
433 if ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||
434 fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
435 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
436 gacopyz_log(SMI_LOG_ERR,
437 _("%s: cannot set close-on-exec: %s"),
438 conn->desc.xxfi_name, strerror(errno));
439 close(fd);
440 return -1;
441 }
442
443 if (addr.sa.sa_family != PF_UNIX
444 && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &yes,
445 sizeof(yes)) == -1) {
446 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
447 gacopyz_log(SMI_LOG_ERR,
448 _("%s: set reuseaddr failed (%s)"),
449 conn->desc.xxfi_name, strerror(errno));
450 close(fd);
451 return -1;
452 }
453
454 old_mask = umask(0117);
455 rc = bind(fd, &addr.sa, socklen);
456 umask(old_mask);
457 if (rc < 0) {
458 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
459 gacopyz_log(SMI_LOG_ERR,
460 _("%s: cannot bind to port %s: %s"),
461 conn->desc.xxfi_name, cstr,
462 strerror(errno));
463 close(fd);
464 return -1;
465 }
466
467 if (listen(fd, backlog)) {
468 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
469 gacopyz_log(SMI_LOG_ERR,
470 _("%s: cannot listen on port %s: %s"),
471 conn->desc.xxfi_name, cstr,
472 strerror(errno));
473 close(fd);
474 return -1;
475 }
476
477
478 return fd;
479 }
480
481 int
gacopyz_open(gacopyz_conn_t conn,const char * cstr,int backlog,int rmsocket)482 gacopyz_open(gacopyz_conn_t conn, const char *cstr, int backlog, int rmsocket)
483 {
484 char *proto;
485 char *port;
486 char *path;
487
488 if (!conn) {
489 gacopyz_log(SMI_LOG_ERR,
490 _("empty or missing socket information"));
491 errno = EINVAL;
492 return MI_FAILURE;
493 }
494
495 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_DEBUG))
496 gacopyz_log(SMI_LOG_DEBUG,
497 _("%s: opening listen socket on %s"),
498 conn->desc.xxfi_name, cstr);
499 if (parse_connection(conn, cstr, &proto, &port, &path)) {
500 errno = ENOENT;
501 return MI_FAILURE;
502 }
503
504 conn->sd = do_connect(conn, cstr, proto, port, path, backlog,
505 rmsocket);
506
507 free(proto);
508 free(port);
509 free(path);
510 return conn->sd == -1 ? MI_FAILURE : MI_SUCCESS;
511 }
512
513 int
gacopyz_get_logmask(gacopyz_conn_t conn,int * mask)514 gacopyz_get_logmask(gacopyz_conn_t conn, int *mask)
515 {
516 if (!conn || !mask)
517 return MI_FAILURE;
518 *mask = conn->desc.logmask;
519 return MI_SUCCESS;
520 }
521
522 int
gacopyz_set_logmask(gacopyz_conn_t conn,int mask)523 gacopyz_set_logmask(gacopyz_conn_t conn, int mask)
524 {
525 if (!conn)
526 return MI_FAILURE;
527 conn->desc.logmask = mask;
528 return MI_SUCCESS;
529 }
530
531 int
gacopyz_set_foreground(gacopyz_conn_t conn,int fg)532 gacopyz_set_foreground(gacopyz_conn_t conn, int fg)
533 {
534 if (!conn)
535 return MI_FAILURE;
536 conn->foreground = fg;
537 return MI_SUCCESS;
538 }
539
540 int
gacopyz_connect(gacopyz_conn_t * pconn,struct smfiDesc * desc,const char * cstr,int backlog,int rmsocket)541 gacopyz_connect(gacopyz_conn_t *pconn, struct smfiDesc *desc,
542 const char *cstr, int backlog, int rmsocket)
543 {
544 gacopyz_conn_t conn;
545
546 if (gacopyz_init(&conn, desc))
547 return MI_FAILURE;
548
549 if (gacopyz_open(conn, cstr, backlog, rmsocket) == -1) {
550 gacopyz_free(conn);
551 return MI_FAILURE;
552 }
553 *pconn = conn;
554 return MI_SUCCESS;
555 }
556
557 int
gacopyz_get_fd(gacopyz_conn_t conn)558 gacopyz_get_fd(gacopyz_conn_t conn)
559 {
560 if (!conn)
561 return -1;
562 return conn->sd;
563 }
564
565 int
gacopyz_set_master_timeout(gacopyz_conn_t conn,struct timeval * timeout)566 gacopyz_set_master_timeout(gacopyz_conn_t conn, struct timeval *timeout)
567 {
568 if (!conn)
569 return MI_FAILURE;
570 conn->master_timeout = *timeout;
571 return MI_SUCCESS;
572 }
573
574 int
gacopyz_get_master_timeout(gacopyz_conn_t conn,struct timeval * timeout)575 gacopyz_get_master_timeout(gacopyz_conn_t conn, struct timeval *timeout)
576 {
577 if (!conn)
578 return MI_FAILURE;
579 *timeout = conn->master_timeout;
580 return MI_SUCCESS;
581 }
582
583 int
gacopyz_set_ctx_timeout(gacopyz_conn_t conn,struct timeval * timeout)584 gacopyz_set_ctx_timeout(gacopyz_conn_t conn, struct timeval *timeout)
585 {
586 if (!conn)
587 return MI_FAILURE;
588 conn->desc.ctx_timeout = *timeout;
589 return MI_SUCCESS;
590 }
591
592 int
gacopyz_get_ctx_timeout(gacopyz_conn_t conn,struct timeval * timeout)593 gacopyz_get_ctx_timeout(gacopyz_conn_t conn, struct timeval *timeout)
594 {
595 if (!conn)
596 return MI_FAILURE;
597 *timeout = conn->desc.ctx_timeout;
598 return MI_SUCCESS;
599 }
600
601 #include "trans.h"
602
603 int
trans_ok(enum state from,enum state to)604 trans_ok(enum state from, enum state to)
605 {
606 while (1) {
607 if (transtab[from][to])
608 return 1;
609 if (++from == st_skip)
610 return 0;
611 if (!transtab[from][st_skip])
612 return 0;
613 }
614 }
615
616 /* Fix up the transition table */
617 static void
trans_fixup(SMFICTX * ctx)618 trans_fixup(SMFICTX *ctx)
619 {
620 transtab[st_conn][st_skip] = !!(ctx->pflags & SMFIP_NOCONNECT);
621 transtab[st_helo][st_skip] = !!(ctx->pflags & SMFIP_NOHELO);
622 transtab[st_mail][st_skip] = !!(ctx->pflags & SMFIP_NOMAIL);
623 transtab[st_rcpt][st_skip] = !!(ctx->pflags & SMFIP_NORCPT);
624 transtab[st_hdrs][st_skip] = !!(ctx->pflags & SMFIP_NOHDRS);
625 transtab[st_eohs][st_skip] = !!(ctx->pflags & SMFIP_NOEOH);
626 transtab[st_body][st_skip] = !!(ctx->pflags & SMFIP_NOBODY);
627 transtab[st_data][st_skip] = !!(ctx->pflags & SMFIP_NODATA);
628 transtab[st_unkn][st_skip] = !!(ctx->pflags & SMFIP_NOUNKNOWN);
629 }
630
631 static int
ctx_read(SMFICTX * ctx,char * buf,size_t size)632 ctx_read(SMFICTX *ctx, char *buf, size_t size)
633 {
634 int rc = MI_SUCCESS;
635
636 while (size) {
637 fd_set rset;
638 fd_set xset;
639 int res;
640 struct timeval to;
641
642 FD_ZERO(&rset);
643 FD_ZERO(&xset);
644 FD_SET(ctx->sd, &rset);
645 FD_SET(ctx->sd, &xset);
646
647 to = ctx->desc->ctx_timeout;
648
649 res = select(ctx->sd + 1, &rset, NULL, &xset, &to);
650 if (res == 0) {
651 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
652 gacopyz_log(SMI_LOG_ERR,
653 _("%s: connection timed out"),
654 "ctx_read");
655 errno = ETIMEDOUT;
656 rc = MI_FAILURE;
657 break;
658 } else if (res < 0) {
659 if (errno == EINTR)
660 continue;
661 rc = MI_FAILURE;
662 break;
663 } else if (rc > 0) {
664 if (FD_ISSET(ctx->sd, &xset)) {
665 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
666 gacopyz_log(SMI_LOG_ERR,
667 _("%s: exception on control fd"),
668 "ctx_read");
669 rc = MI_FAILURE;
670 break;
671 }
672 /* Otherwise, FD_ISSET(ctx->sd, &rset) is true */
673 }
674
675 res = read(ctx->sd, buf, size);
676 if (res == -1) {
677 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
678 gacopyz_log(SMI_LOG_ERR,
679 _("%s: read failed: %s"),
680 "ctx_read",
681 strerror(errno));
682 rc = MI_FAILURE;
683 break;
684 } else if (res == 0) {
685 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
686 gacopyz_log(SMI_LOG_ERR,
687 _("%s: end of file"),
688 "ctx_read");
689 rc = MI_FAILURE;
690 break;
691 }
692
693 buf += res;
694 size -= res;
695 }
696 return rc;
697 }
698
699 static int
ctx_write(SMFICTX * ctx,const char * buf,size_t size)700 ctx_write(SMFICTX *ctx, const char *buf, size_t size)
701 {
702 int rc = MI_SUCCESS;
703
704 while (size) {
705 fd_set wset;
706 fd_set xset;
707 int res;
708 struct timeval to;
709
710 FD_ZERO(&wset);
711 FD_ZERO(&xset);
712 FD_SET(ctx->sd, &wset);
713 FD_SET(ctx->sd, &xset);
714
715 to = ctx->desc->ctx_timeout;
716
717 res = select(ctx->sd + 1, NULL, &wset, &xset, &to);
718 if (res == 0) {
719 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
720 gacopyz_log(SMI_LOG_ERR,
721 _("%s: connection timed out"),
722 "ctx_write");
723 errno = ETIMEDOUT;
724 rc = MI_FAILURE;
725 break;
726 } else if (res < 0) {
727 if (errno == EINTR)
728 continue;
729 rc = MI_FAILURE;
730 break;
731 } else if (rc > 0) {
732 if (FD_ISSET(ctx->sd, &xset)) {
733 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
734 gacopyz_log(SMI_LOG_ERR,
735 _("%s: exception on control fd"),
736 "ctx_write");
737 rc = MI_FAILURE;
738 break;
739 }
740 /* Otherwise, FD_ISSET(ctx->sd, &wset) is true */
741 }
742
743 res = write(ctx->sd, buf, size);
744 if (res == -1) {
745 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
746 gacopyz_log(SMI_LOG_ERR,
747 _("%s: write failed: %s"),
748 "ctx_write",
749 strerror(errno));
750 rc = MI_FAILURE;
751 break;
752 } else if (res == 0) {
753 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
754 gacopyz_log(SMI_LOG_ERR,
755 _("%s: wrote 0 bytes"),
756 "ctx_write");
757 rc = MI_FAILURE;
758 break;
759 }
760
761 buf += res;
762 size -= res;
763 }
764 return rc;
765 }
766
767
768 union header {
769 struct {
770 gacopyz_uint32_t size;
771 unsigned char cmd;
772 } hdr;
773 char buf[5];
774 };
775
776 static int
get_command(SMFICTX * ctx,unsigned char * cmd,size_t * pcount,char ** pbuf,size_t * psize)777 get_command(SMFICTX *ctx, unsigned char *cmd, size_t *pcount,
778 char **pbuf, size_t *psize)
779 {
780 union header header;
781 size_t size;
782 int rc;
783
784 if ((rc = ctx_read(ctx, header.buf, sizeof header.buf)) != MI_SUCCESS)
785 return rc;
786
787 size = ntohl(header.hdr.size) - 1;
788 if (size + 1 > *psize) {
789 char *p = realloc(*pbuf, size + 1);
790 if (!p) {
791 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
792 gacopyz_log(SMI_LOG_ERR, "%s",
793 strerror(errno));
794 return MI_FAILURE;
795 }
796 *pbuf = p;
797 *psize = size + 1;
798 }
799
800 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_DEBUG))
801 gacopyz_log(SMI_LOG_DEBUG,
802 _("read header: size=%lu, cmd=%c"),
803 size, header.hdr.cmd);
804
805 if ((rc = ctx_read(ctx, *pbuf, size)) != MI_SUCCESS)
806 return rc;
807 (*pbuf)[size] = 0;
808
809 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_PROTO))
810 gacopyz_logdump(SMI_LOG_PROTO, _("read data"), *pbuf, size);
811 else if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_DEBUG))
812 gacopyz_log(SMI_LOG_DEBUG, _("read data"));
813 *pcount = size;
814 *cmd = header.hdr.cmd;
815 return MI_SUCCESS;
816 }
817
818 #define _GACOPYZ_R_NOREPLY 0
819
820 static unsigned char
convert_sfsistat(sfsistat stat)821 convert_sfsistat(sfsistat stat)
822 {
823 switch (stat) {
824 case SMFIS_CONTINUE:
825 return SMFIR_CONTINUE;
826 case SMFIS_REJECT:
827 return SMFIR_REJECT;
828 case SMFIS_DISCARD:
829 return SMFIR_DISCARD;
830 case SMFIS_ACCEPT:
831 return SMFIR_ACCEPT;
832 case SMFIS_TEMPFAIL:
833 return SMFIR_TEMPFAIL;
834 case SMFIS_NOREPLY:
835 return _GACOPYZ_R_NOREPLY;
836 case SMFIS_SKIP:
837 return SMFIR_SKIP;
838 default:
839 break;
840 }
841 return SMFIR_TEMPFAIL; /* Just in case */
842 }
843
844 #define OPTLEN 3*sizeof(gacopyz_uint32_t)
845
846 static int
make_optneg_buf(SMFICTX * ctx,gacopyz_uint32_t * vbuf,size_t * psize,char ** pbuf)847 make_optneg_buf(SMFICTX *ctx, gacopyz_uint32_t *vbuf,
848 size_t *psize, char **pbuf)
849 {
850 char *buf;
851 size_t bufsize = 0;
852 int i;
853
854 for (i = 0; i < gacopyz_stage_max; i++) {
855 if (ctx->req_macros[i])
856 bufsize += strlen(ctx->req_macros[i]) + 1 +
857 sizeof(gacopyz_uint32_t);
858 }
859
860 if (bufsize == 0)
861 buf = (char*) vbuf;
862 else {
863 bufsize += OPTLEN;
864 buf = malloc(bufsize);
865 if (!buf)
866 return MI_FAILURE;
867 vbuf = (gacopyz_uint32_t*)buf;
868 }
869 vbuf[0] = htonl(ctx->version);
870 vbuf[1] = htonl(ctx->aflags);
871 vbuf[2] = htonl(ctx->pflags);
872
873 if (bufsize) {
874 /*char *endp = buf + bufsize;*/
875 *psize = bufsize;
876 *pbuf = buf;
877
878 buf += OPTLEN;
879 for (i = 0; i < gacopyz_stage_max; i++) {
880 if (ctx->req_macros[i]) {
881 gacopyz_uint32_t v;
882 size_t len;
883
884 v = htonl(i);
885 memcpy(buf, &v, sizeof(v));
886 buf += sizeof(v);
887 len = strlen(ctx->req_macros[i]) + 1;
888 memcpy(buf, ctx->req_macros[i], len);
889 buf += len;
890 }
891 }
892 }
893 return MI_SUCCESS;
894 }
895
896 static int
send_reply(SMFICTX * ctx,unsigned char cmd)897 send_reply(SMFICTX *ctx, unsigned char cmd)
898 {
899 int rc;
900 union header header;
901 char *buf = NULL;
902 size_t bufsize = 0;
903 char *alloc_mem = NULL;
904 gacopyz_uint32_t v[3];
905 unsigned long nrmask = state_nr_mask[ctx->state];
906
907 if (nrmask && (ctx->pflags & nrmask) && cmd != _GACOPYZ_R_NOREPLY) {
908 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_WARN))
909 gacopyz_log(SMI_LOG_WARN,
910 _("%s: milter claimed not to reply in state %d, but it did"),
911 ctx->desc->xxfi_name,
912 ctx->state);
913 cmd = _GACOPYZ_R_NOREPLY;
914 }
915
916 switch (cmd) {
917 case _GACOPYZ_R_NOREPLY:
918 if (nrmask && (ctx->pflags & nrmask)) {
919 if (ctx->mta_pflags & nrmask)
920 return 0;
921 else
922 cmd = SMFIR_CONTINUE;
923 }
924 break;
925
926 case SMFIR_CONTINUE:
927 break;
928
929 case SMFIR_TEMPFAIL:
930 if (ctx->reply && *ctx->reply == '4') {
931 cmd = SMFIR_REPLYCODE;
932 buf = ctx->reply;
933 bufsize = strlen(ctx->reply) + 1;
934 }
935 break;
936
937 case SMFIR_REJECT:
938 if (ctx->reply && *ctx->reply == '5') {
939 cmd = SMFIR_REPLYCODE;
940 buf = ctx->reply;
941 bufsize = strlen(ctx->reply) + 1;
942 }
943 break;
944
945 case SMFIR_DISCARD:
946 break;
947
948 case SMFIR_ACCEPT:
949 break;
950
951 case SMFIC_OPTNEG:
952 buf = (char*) v;
953 bufsize = sizeof v;
954 if (make_optneg_buf(ctx, v, &bufsize, &alloc_mem)
955 != MI_SUCCESS)
956 return MI_FAILURE;
957 if (alloc_mem)
958 buf = alloc_mem;
959 break;
960
961 default: /* Ignore */
962 break;
963 }
964
965 TRACE(ctx, cmd, bufsize, buf);
966
967 header.hdr.size = htonl(bufsize + 1);
968 header.hdr.cmd = cmd;
969 rc = ctx_write(ctx, header.buf, sizeof header.buf);
970 if (rc == MI_SUCCESS) {
971 if (bufsize)
972 rc = ctx_write(ctx, buf, bufsize);
973 }
974 if (alloc_mem)
975 free(alloc_mem);
976 return rc;
977 }
978
979 static void
macro_assoc_free(macro_assoc_t * p)980 macro_assoc_free(macro_assoc_t *p)
981 {
982 free(p->argv);
983 free(p->buffer);
984 p->argv = NULL;
985 p->buffer = NULL;
986 }
987
988 static void
clear_macros(SMFICTX * ctx,int i)989 clear_macros(SMFICTX *ctx, int i)
990 {
991 for (; i < gacopyz_stage_max; i++)
992 macro_assoc_free(&ctx->macros[i]);
993 }
994
995 enum state_arg_type {
996 arg_no_args, /* no arguments */
997 arg_one_string, /* one string */
998 arg_two_strings, /* two strings */
999 arg_ints, /* three integers */
1000 arg_argv, /* NULL-terminated, \0 separated list of
1001 arguments */
1002 arg_argvc /* 1 byte command + arg_argv */
1003 };
1004
1005 union state_arg {
1006 struct {
1007 char *ptr;
1008 size_t len;
1009 } string;
1010 char *strings[2];
1011 gacopyz_uint32_t ints[3];
1012 struct {
1013 char cmd;
1014 char **v;
1015 char *buffer;
1016 } argv;
1017 };
1018
1019 typedef enum {
1020 sret_fail,
1021 sret_abort,
1022 sret_noreply,
1023 sret_reply,
1024 sret_reject
1025 } state_ret_type;
1026
1027 typedef state_ret_type (*state_handler_fn) (SMFICTX *,
1028 union state_arg *,
1029 unsigned char *);
1030
1031 struct state_disp {
1032 int cmd;
1033 char *name;
1034 enum state_arg_type arg_type;
1035 state_handler_fn fn;
1036 enum state next;
1037 int flags;
1038 int macro_ind;
1039 };
1040
1041 static state_ret_type
shan_abort(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1042 shan_abort(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1043 {
1044 if (ctx->desc->xxfi_abort)
1045 ctx->desc->xxfi_abort(ctx);
1046 return sret_noreply;
1047 }
1048
1049 static state_ret_type
shan_macro(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1050 shan_macro(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1051 {
1052 enum gacopyz_stage ind;
1053 char **p;
1054
1055 if (!arg->argv.v)
1056 return sret_noreply;
1057
1058 switch (arg->argv.cmd) {
1059 case SMFIC_CONNECT:
1060 ind = gacopyz_stage_conn;
1061 break;
1062
1063 case SMFIC_HELO:
1064 ind = gacopyz_stage_helo;
1065 break;
1066
1067 case SMFIC_MAIL:
1068 ind = gacopyz_stage_mail;
1069 break;
1070
1071 case SMFIC_RCPT:
1072 ind = gacopyz_stage_rcpt;
1073 break;
1074
1075 case SMFIC_DATA:
1076 ind = gacopyz_stage_data;
1077 break;
1078
1079 case SMFIC_BODYEOB:
1080 ind = gacopyz_stage_eom;
1081 break;
1082
1083 case SMFIC_EOH:
1084 ind = gacopyz_stage_eoh;
1085 break;
1086
1087 default:
1088 return sret_fail;
1089 }
1090
1091 macro_assoc_free(&ctx->macros[ind]);
1092 ctx->macros[ind].argv = arg->argv.v;
1093 arg->argv.v = NULL;
1094 ctx->macros[ind].buffer = arg->argv.buffer;
1095 arg->argv.buffer = NULL;
1096 for (p = ctx->macros[ind].argv; *p; p += 2) {
1097 int len;
1098 if ((*p)[0] == '{')
1099 ++*p;
1100 len = strlen(*p);
1101 if (len > 0 && (*p)[len - 1] == '}')
1102 (*p)[len - 1] = 0;
1103 }
1104 return sret_noreply;
1105 }
1106
1107 static state_ret_type
shan_body(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1108 shan_body(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1109 {
1110 if (ctx->desc->xxfi_body) {
1111 *cmd = convert_sfsistat(
1112 ctx->desc->xxfi_body(ctx,
1113 (unsigned char*) arg->string.ptr,
1114 arg->string.len));
1115 } else
1116 *cmd = SMFIR_CONTINUE;
1117 return sret_reply;
1118 }
1119
1120 static state_ret_type
shan_connect(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1121 shan_connect(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1122 {
1123 char *s;
1124 size_t len;
1125 int family;
1126 milter_sockaddr_t sockaddr;
1127 unsigned short port = 0;
1128
1129 *cmd = SMFIR_CONTINUE;
1130 if (!ctx->desc->xxfi_connect)
1131 return sret_reply;
1132
1133 memset(&sockaddr, 0, sizeof sockaddr);
1134
1135 s = arg->strings[1];
1136 family = *s++;
1137 if (family != SMFIA_UNKNOWN) {
1138 port = *(unsigned short*) s;
1139 s += sizeof port;
1140
1141 len = strlen(s);
1142
1143 if (len > 0 && s[len])
1144 return sret_abort;
1145
1146 switch (family) {
1147 case SMFIA_UNIX:
1148 if (len >= sizeof sockaddr.sunix.sun_path) {
1149 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
1150 gacopyz_log(SMI_LOG_ERR,
1151 "%s: shan_connect: %s: %lu",
1152 ctx->desc->xxfi_name,
1153 _("path too long"),
1154 (unsigned long) len);
1155 return sret_abort;
1156 }
1157 strcpy(sockaddr.sunix.sun_path, s);
1158 sockaddr.sunix.sun_family = AF_UNIX;
1159 break;
1160
1161 case SMFIA_INET:
1162 if (inet_aton(s,
1163 (struct in_addr *) &sockaddr.sin.sin_addr) != 1) {
1164 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
1165 gacopyz_log(SMI_LOG_ERR,
1166 _("%s: shan_connect: inet_aton(%s) failed"),
1167 ctx->desc->xxfi_name,
1168 s);
1169 return sret_abort;
1170 }
1171 sockaddr.sa.sa_family = AF_INET;
1172 sockaddr.sin.sin_port = port;
1173 break;
1174
1175 case SMFIA_INET6: {
1176 #ifdef GACOPYZ_IPV6
1177 int rc;
1178 struct addrinfo hints;
1179 struct addrinfo *res;
1180 char service[64];
1181
1182 if (len > GACOPYZ_IPV6PREFIX_LEN &&
1183 memcmp (s, GACOPYZ_IPV6PREFIX_STR,
1184 GACOPYZ_IPV6PREFIX_LEN) == 0) {
1185 s += GACOPYZ_IPV6PREFIX_LEN;
1186 len -= GACOPYZ_IPV6PREFIX_LEN;
1187 }
1188
1189 memset(&hints, 0, sizeof(hints));
1190 hints.ai_family = AF_INET6;
1191 hints.ai_socktype = SOCK_STREAM;
1192 snprintf(service, sizeof service, "%hu", ntohs(port));
1193 rc = getaddrinfo(s, service, &hints, &res);
1194
1195 switch (rc) {
1196 case 0:
1197 break;
1198
1199 case EAI_SYSTEM:
1200 gacopyz_log(SMI_LOG_ERR,
1201 _("%s:%s: cannot parse "
1202 "address: %s"),
1203 s, service, strerror(errno));
1204 return sret_abort;
1205
1206 case EAI_BADFLAGS:
1207 case EAI_SOCKTYPE:
1208 gacopyz_log(SMI_LOG_ERR,
1209 _("%s:%d: internal error "
1210 "converting %s:%s"),
1211 __FILE__, __LINE__, s, service);
1212 return sret_abort;
1213
1214 case EAI_MEMORY:
1215 gacopyz_log(SMI_LOG_ERR, "not enogh memory");
1216 return sret_abort;
1217
1218 default:
1219 gacopyz_log(SMI_LOG_ERR,
1220 "%s:%s: %s",
1221 s, service, gai_strerror(rc));
1222 return sret_abort;
1223 }
1224
1225 if (res->ai_addrlen > sizeof(sockaddr)) {
1226 gacopyz_log(SMI_LOG_ERR,
1227 _("%s:%s: address length too "
1228 "big (%lu)"),
1229 s, service,
1230 (unsigned long) res->ai_addrlen);
1231 freeaddrinfo(res);
1232 return sret_abort;
1233 }
1234 memcpy(&sockaddr, res->ai_addr, res->ai_addrlen);
1235 freeaddrinfo(res);
1236 break;
1237 #endif
1238 }
1239
1240 default:
1241 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
1242 gacopyz_log(SMI_LOG_ERR,
1243 _("%s: shan_connect: unknown family %d"),
1244 ctx->desc->xxfi_name,
1245 family);
1246 return sret_abort;
1247 }
1248 }
1249
1250 *cmd = convert_sfsistat(
1251 ctx->desc->xxfi_connect(ctx,
1252 arg->strings[0],
1253 family != SMFIA_UNKNOWN ?
1254 &sockaddr : NULL));
1255 return sret_reply;
1256 }
1257
1258 static state_ret_type
shan_endm(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1259 shan_endm(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1260 {
1261 if (arg->string.len && ctx->desc->xxfi_body) {
1262 sfsistat r =
1263 ctx->desc->xxfi_body(ctx,
1264 (unsigned char*) arg->string.ptr,
1265 arg->string.len);
1266 if (r != SMFIS_CONTINUE
1267 && send_reply(ctx, convert_sfsistat(r)) != MI_SUCCESS)
1268 return sret_abort;
1269 }
1270 if (ctx->desc->xxfi_eom)
1271 *cmd = convert_sfsistat(ctx->desc->xxfi_eom(ctx));
1272 else
1273 *cmd = SMFIR_CONTINUE;
1274 return sret_reply;
1275 }
1276
1277 static state_ret_type
shan_helo(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1278 shan_helo(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1279 {
1280 if (ctx->desc->xxfi_helo)
1281 *cmd = convert_sfsistat(
1282 ctx->desc->xxfi_helo(ctx, arg->string.ptr));
1283 else
1284 *cmd = SMFIR_CONTINUE;
1285 return sret_reply;
1286 }
1287
1288 static state_ret_type
shan_header(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1289 shan_header(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1290 {
1291 if (ctx->desc->xxfi_header)
1292 *cmd = convert_sfsistat(
1293 ctx->desc->xxfi_header(ctx,
1294 arg->strings[0],
1295 arg->strings[1]));
1296 else
1297 *cmd = SMFIR_CONTINUE;
1298 return sret_reply;
1299 }
1300
1301 static state_ret_type
shan_mail(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1302 shan_mail(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1303 {
1304 if (ctx->desc->xxfi_envfrom) {
1305 *cmd = convert_sfsistat(
1306 ctx->desc->xxfi_envfrom(ctx, arg->argv.v));
1307 } else
1308 *cmd = SMFIR_CONTINUE;
1309 return sret_reply;
1310 }
1311
1312 #define ALL_NR_FLAGS \
1313 (SMFIP_NR_CONN|\
1314 SMFIP_NR_HELO|\
1315 SMFIP_NR_MAIL|\
1316 SMFIP_NR_RCPT|\
1317 SMFIP_NR_DATA|\
1318 SMFIP_NR_UNKN|\
1319 SMFIP_NR_HDR|\
1320 SMFIP_NR_EOH|\
1321 SMFIP_NR_BODY)
1322
1323
1324 static state_ret_type
shan_optneg(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1325 shan_optneg(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1326 {
1327 gacopyz_uint32_t val;
1328 unsigned long mta_aflags, mta_pflags;
1329
1330 val = arg->ints[0];
1331 if (val < SMFI_PROT_VERSION_MIN) {
1332 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
1333 gacopyz_log(SMI_LOG_ERR,
1334 _("protocol version too old: %lu"),
1335 val);
1336 return sret_abort;
1337 }
1338 if (val > SMFI_PROT_VERSION)
1339 val = SMFI_PROT_VERSION;
1340 ctx->version = val;
1341
1342 val = arg->ints[1];
1343 if (!val)
1344 val = SMFI_V1_ACTS;
1345 mta_aflags = val;
1346
1347 val = arg->ints[2];
1348 if (!val)
1349 val = SMFI_V1_PROT;
1350 ctx->mta_pflags = mta_pflags = val;
1351
1352 ctx->aflags = ctx->desc->xxfi_flags;
1353 if (ctx->version > SMFI_PROT_VERSION_MIN
1354 && ctx->desc->xxfi_negotiate) {
1355 int res;
1356 unsigned long m_aflags = mta_aflags, m_pflags = ctx->pflags;
1357 unsigned long m_f2 = 0, m_f3 = 0; /* reserved */
1358
1359 if (mta_pflags & SMFIP_SKIP)
1360 m_pflags |= SMFIP_SKIP;
1361 res = ctx->desc->xxfi_negotiate(ctx,
1362 mta_aflags,
1363 mta_pflags|ALL_NR_FLAGS,
1364 0, 0,
1365 &m_aflags, &m_pflags,
1366 &m_f2, &m_f3);
1367 switch (res) {
1368 case SMFIS_ALL_OPTS:
1369 ctx->aflags = mta_aflags;
1370 /* ctx->pflags not changed */
1371 if (mta_pflags & SMFIP_SKIP)
1372 ctx->pflags |= SMFIP_SKIP;
1373 break;
1374
1375 case SMFIS_CONTINUE:
1376 ctx->aflags = m_aflags;
1377 ctx->pflags = m_pflags;
1378 break;
1379
1380 default:
1381 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
1382 gacopyz_log(SMI_LOG_ERR,
1383 _("xxfi_negotiate returned %d (protocol options=0x%lx, actions=0x%lx)"),
1384 res, mta_pflags, mta_aflags);
1385 return sret_abort;
1386 }
1387
1388 if ((mta_pflags & ctx->pflags) != ctx->pflags) {
1389 unsigned i;
1390
1391 for (i = 0; i < 32; i++) {
1392 unsigned long bit = 1 << i;
1393 if ((mta_pflags & bit) != bit
1394 && (bit & ALL_NR_FLAGS) == bit)
1395 ctx->pflags &= ~bit;
1396 }
1397 }
1398 }
1399
1400 if ((mta_aflags & ctx->aflags) != ctx->aflags) {
1401 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
1402 gacopyz_log(SMI_LOG_ERR,
1403 _("%s: shan_optneg: MTA flags %#lx do not match "
1404 "actions requirements %#lx"),
1405 ctx->desc->xxfi_name,
1406 mta_aflags,
1407 ctx->aflags);
1408 return sret_abort;
1409 }
1410
1411 if ((mta_pflags & ctx->pflags) != ctx->pflags) {
1412 /* Disable protocol steps not supported by older MTAs */
1413 if ((ctx->pflags & SMFIP_NODATA)
1414 && !(mta_pflags & SMFIP_NODATA))
1415 ctx->pflags &= ~SMFIP_NODATA;
1416
1417 if ((ctx->pflags & SMFIP_NOUNKNOWN)
1418 && !(mta_pflags & SMFIP_NOUNKNOWN))
1419 ctx->pflags &= ~SMFIP_NOUNKNOWN;
1420 }
1421
1422 if ((mta_pflags & ctx->pflags) != ctx->pflags) {
1423 if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR))
1424 gacopyz_log(SMI_LOG_ERR,
1425 _("%s: shan_optneg: MTA flags %#lx do not match "
1426 "protocol requirements %#lx"),
1427 ctx->desc->xxfi_name,
1428 mta_pflags,
1429 ctx->pflags);
1430 return sret_abort;
1431 }
1432
1433 if (GACOPYZ_DESC_LOG_MATCH(ctx->desc, SMI_LOG_DEBUG))
1434 gacopyz_log(SMI_LOG_DEBUG,
1435 "version=%#lx, mta_aflags=%#lx, mta_pflags=%#lx"
1436 " aflags=%#lx, pflags=%#lx",
1437 ctx->version,
1438 mta_aflags, mta_pflags,
1439 ctx->aflags, ctx->pflags);
1440 trans_fixup(ctx);
1441
1442 *cmd = SMFIC_OPTNEG;
1443 return sret_reply;
1444 }
1445
1446 static state_ret_type
shan_eoh(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1447 shan_eoh(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1448 {
1449 if (ctx->desc->xxfi_eoh)
1450 *cmd = convert_sfsistat(ctx->desc->xxfi_eoh(ctx));
1451 else
1452 *cmd = SMFIR_CONTINUE;
1453 return sret_reply;
1454 }
1455
1456 static state_ret_type
shan_quit(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1457 shan_quit(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1458 {
1459 return sret_noreply;
1460 }
1461
1462 static state_ret_type
shan_data(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1463 shan_data(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1464 {
1465 if (ctx->desc->xxfi_data)
1466 *cmd = convert_sfsistat(ctx->desc->xxfi_data(ctx));
1467 else
1468 *cmd = SMFIR_CONTINUE;
1469 return sret_reply;
1470 }
1471
1472 static state_ret_type
shan_rcpt(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1473 shan_rcpt(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1474 {
1475 if (ctx->desc->xxfi_envrcpt) {
1476 *cmd = convert_sfsistat(
1477 ctx->desc->xxfi_envrcpt(ctx, arg->argv.v));
1478 } else
1479 *cmd = SMFIR_CONTINUE;
1480 return sret_reply;
1481 }
1482
1483 static state_ret_type
shan_unkn(SMFICTX * ctx,union state_arg * arg,unsigned char * cmd)1484 shan_unkn(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
1485 {
1486 if (ctx->desc->xxfi_unknown) {
1487 *cmd = convert_sfsistat(
1488 ctx->desc->xxfi_unknown(ctx, arg->string.ptr));
1489 } else
1490 *cmd = SMFIR_CONTINUE;
1491 return sret_reply;
1492 }
1493
1494 #define CT_CNT 0
1495 #define CT_END 0x1
1496 #define CT_IGN 0x2
1497 #define CT_CLR 0x4
1498
1499 static struct state_disp disp[] = {
1500 { SMFIC_ABORT, "abort",
1501 arg_no_args, shan_abort, st_abrt, CT_CNT, gacopyz_stage_none },
1502 { SMFIC_MACRO, "macro",
1503 arg_argvc, shan_macro, st_none, CT_CNT, gacopyz_stage_none },
1504 { SMFIC_BODY, "body",
1505 arg_one_string, shan_body, st_body, CT_CNT, gacopyz_stage_none },
1506 { SMFIC_CONNECT, "connect",
1507 arg_two_strings, shan_connect, st_conn, CT_CLR, gacopyz_stage_conn },
1508 { SMFIC_BODYEOB, "endm",
1509 arg_one_string, shan_endm, st_endm, CT_CNT, gacopyz_stage_eom },
1510 { SMFIC_HELO, "helo",
1511 arg_one_string, shan_helo, st_helo, CT_CLR, gacopyz_stage_helo },
1512 { SMFIC_HEADER, "header",
1513 arg_two_strings, shan_header, st_hdrs, CT_CNT, gacopyz_stage_none },
1514 { SMFIC_MAIL, "mail",
1515 arg_argv, shan_mail, st_mail, CT_CLR, gacopyz_stage_mail },
1516 { SMFIC_OPTNEG, "optneg",
1517 arg_ints, shan_optneg, st_opts, CT_CNT, gacopyz_stage_none },
1518 { SMFIC_EOH, "eoh",
1519 arg_no_args, shan_eoh, st_eohs, CT_CNT, gacopyz_stage_none },
1520 { SMFIC_QUIT, "quit",
1521 arg_no_args, shan_quit, st_quit, CT_END, gacopyz_stage_none },
1522 { SMFIC_DATA, "data",
1523 arg_no_args, shan_data, st_data, CT_CNT, gacopyz_stage_none },
1524 { SMFIC_RCPT, "rcpt",
1525 arg_argv, shan_rcpt, st_rcpt, CT_IGN|CT_CLR, gacopyz_stage_rcpt },
1526 { SMFIC_UNKNOWN, "unknown",
1527 arg_one_string, shan_unkn, st_unkn, CT_IGN|CT_CLR, gacopyz_stage_none }
1528 };
1529
1530 struct state_disp *
find_disp(int cmd,int allow_unkn)1531 find_disp(int cmd, int allow_unkn)
1532 {
1533 struct state_disp *def = NULL;
1534 struct state_disp *sd;
1535 for (sd = disp; sd < disp + sizeof(disp)/sizeof(disp[0]); sd++) {
1536 if (sd->cmd == cmd)
1537 return sd;
1538 if (allow_unkn && sd->cmd == SMFIC_UNKNOWN)
1539 def = sd;
1540 }
1541 return def;
1542 }
1543
1544 static int
parse_state_arg(union state_arg * arg,enum state_arg_type type,char * buffer,size_t size)1545 parse_state_arg(union state_arg *arg, enum state_arg_type type,
1546 char *buffer, size_t size)
1547 {
1548 switch (type) {
1549 case arg_no_args: /* no arguments */
1550 return MI_SUCCESS;
1551
1552 case arg_one_string: /* one string */
1553 arg->string.ptr = buffer;
1554 arg->string.len = size;
1555 return MI_SUCCESS;
1556
1557 case arg_two_strings: /* two strings */
1558 {
1559 int len = strlen(buffer);
1560 arg->strings[0] = buffer;
1561 arg->strings[1] = buffer + len + 1;
1562 return MI_SUCCESS;
1563 }
1564
1565 case arg_ints: /* three integers */
1566 {
1567 if (size < GACOPYZ_OPTLEN)
1568 return MI_FAILURE;
1569
1570 arg->ints[0] = ntohl(((gacopyz_uint32_t*)buffer)[0]);
1571 arg->ints[1] = ntohl(((gacopyz_uint32_t*)buffer)[1]);
1572 arg->ints[2] = ntohl(((gacopyz_uint32_t*)buffer)[2]);
1573 return MI_SUCCESS;
1574 }
1575
1576 case arg_argv: /* NULL-terminated, \0 separated list of strings */
1577 case arg_argvc:
1578 {
1579 size_t i, count = 0;
1580 char *p;
1581
1582 if (type == arg_argvc) {
1583 arg->argv.cmd = *buffer;
1584 buffer++;
1585 size--;
1586 }
1587 arg->argv.buffer = malloc(size);
1588 if (!arg->argv.buffer)
1589 return MI_FAILURE;
1590 memcpy(arg->argv.buffer, buffer, size);
1591
1592 for (p = arg->argv.buffer; p < arg->argv.buffer + size; p++)
1593 if (*p == 0)
1594 count++;
1595 if (count == 0) {
1596 arg->argv.v = NULL;
1597 return MI_SUCCESS;
1598 }
1599 arg->argv.v = calloc(count + 1, sizeof arg->argv.v[0]);
1600 if (!arg->argv.v) {
1601 free(arg->argv.buffer);
1602 return MI_FAILURE;
1603 }
1604
1605 for (i = 0, p = arg->argv.buffer; i < count;
1606 i++, p += strlen(p) + 1)
1607 arg->argv.v[i] = p;
1608 arg->argv.v[i] = NULL;
1609 return MI_SUCCESS;
1610 }
1611 }
1612 return MI_FAILURE;
1613 }
1614
1615 static void
arg_free(union state_arg * arg,enum state_arg_type type)1616 arg_free(union state_arg *arg, enum state_arg_type type)
1617 {
1618 if (type == arg_argv) {
1619 free(arg->argv.v);
1620 free(arg->argv.buffer);
1621 }
1622 }
1623
1624 /* in a mail transaction? must be before eom according to spec. */
1625 #define ST_IN_MAIL(st) ((st) >= st_mail && (st) < st_endm)
1626 #define STATE_NAME(st) ((st == st_none) ? "st_none" : state_name[st])
1627
1628 static void
report_command(enum state state,enum state next_state,int cmd,char * buffer,size_t size)1629 report_command(enum state state, enum state next_state,
1630 int cmd, char *buffer, size_t size)
1631 {
1632 gacopyz_log(SMI_LOG_DEBUG,
1633 _("state %s is unreachable from %s"),
1634 STATE_NAME(next_state), STATE_NAME(state));
1635 gacopyz_log(SMI_LOG_DEBUG,
1636 isascii(cmd) && isprint(cmd)
1637 ? _("ignoring milter command: %c")
1638 : _("ignoring milter command: \\%03o"),
1639 (unsigned char) cmd);
1640 gacopyz_logdump(SMI_LOG_DEBUG, _("buffer"), buffer, size);
1641 }
1642
1643 static void
ctx_free(SMFICTX * ctx)1644 ctx_free(SMFICTX *ctx)
1645 {
1646 int i;
1647
1648 free(ctx->reply);
1649 clear_macros(ctx, 0);
1650 for (i = 0; i < gacopyz_stage_max; i++)
1651 free(ctx->req_macros[i]);
1652 }
1653
1654 int
gacopyz_context_loop(int fd,struct smfiDesc const * desc,milter_sockaddr_t * addr,socklen_t addrlen,void * closure)1655 gacopyz_context_loop(int fd, struct smfiDesc const *desc,
1656 milter_sockaddr_t *addr, socklen_t addrlen,
1657 void *closure)
1658 {
1659 int rc;
1660 char *buffer = NULL;
1661 size_t bufsize = 0;
1662 size_t size;
1663 unsigned char cmd;
1664 SMFICTX ctx;
1665 int allow_unkn = desc->xxfi_unknown != NULL;
1666 enum state state = st_init;
1667 int stop = 0;
1668
1669 if (desc->xxfi_start)
1670 desc->xxfi_start();
1671
1672 memset(&ctx, 0, sizeof ctx);
1673 ctx.desc = desc;
1674 ctx.sd = fd;
1675 ctx.addr = *addr;
1676 ctx.addrlen = addrlen;
1677 ctx.closure = closure;
1678 if (!desc->xxfi_connect)
1679 ctx.pflags |= SMFIP_NOCONNECT;
1680 if (!desc->xxfi_helo)
1681 ctx.pflags |= SMFIP_NOHELO;
1682 if (!desc->xxfi_envfrom)
1683 ctx.pflags |= SMFIP_NOMAIL;
1684 if (!desc->xxfi_envrcpt)
1685 ctx.pflags |= SMFIP_NORCPT;
1686 if (!desc->xxfi_header)
1687 ctx.pflags |= SMFIP_NOHDRS;
1688 if (!desc->xxfi_eoh)
1689 ctx.pflags |= SMFIP_NOEOH;
1690 if (!desc->xxfi_body)
1691 ctx.pflags |= SMFIP_NOBODY;
1692 if (!desc->xxfi_data)
1693 ctx.pflags |= SMFIP_NODATA;
1694 if (!desc->xxfi_unknown)
1695 ctx.pflags |= SMFIP_NOUNKNOWN;
1696
1697 trans_fixup(&ctx);
1698
1699 if (GACOPYZ_DESC_LOG_MATCH(desc, SMI_LOG_DEBUG))
1700 gacopyz_log(SMI_LOG_DEBUG, _("start of context loop"));
1701
1702 while (!stop
1703 && (rc = get_command(&ctx, &cmd, &size, &buffer, &bufsize))
1704 == MI_SUCCESS) {
1705 enum state next_state;
1706 state_ret_type ret;
1707 union state_arg arg;
1708 struct state_disp *sd = find_disp(cmd, allow_unkn);
1709
1710 if (sd == NULL) {
1711 if (GACOPYZ_DESC_LOG_MATCH(desc, SMI_LOG_ERR)) {
1712 gacopyz_log(SMI_LOG_ERR,
1713 isascii(cmd) && isprint(cmd)
1714 ? _("unknown command: %c")
1715 : _("unknown command: \\%03o"),
1716 (unsigned char) cmd);
1717 gacopyz_logdump(SMI_LOG_ERR,
1718 _("buffer"),
1719 buffer, size);
1720 }
1721 rc = MI_FAILURE;
1722 break;
1723 }
1724
1725 next_state = sd->next;
1726 stop = sd->flags & CT_END;
1727
1728 if (GACOPYZ_DESC_LOG_MATCH(desc, SMI_LOG_DEBUG))
1729 gacopyz_log(SMI_LOG_DEBUG,
1730 _("state %s, next state %s"),
1731 STATE_NAME(state),
1732 next_state == st_none ?
1733 STATE_NAME(state) :
1734 STATE_NAME(next_state));
1735
1736 if (next_state != st_none && !trans_ok(state, next_state)) {
1737 if (GACOPYZ_DESC_LOG_MATCH(desc, SMI_LOG_DEBUG))
1738 gacopyz_log(SMI_LOG_DEBUG,
1739 _("transition from state %s to %s is "
1740 "prohibited"),
1741 STATE_NAME(state),
1742 STATE_NAME(next_state));
1743
1744 if (ST_IN_MAIL(state) && desc->xxfi_abort)
1745 desc->xxfi_abort(&ctx);
1746
1747 /* Retry starting from HELO */
1748 state = st_helo;
1749 if (!trans_ok(state, next_state)) {
1750 if (GACOPYZ_DESC_LOG_MATCH(desc,
1751 SMI_LOG_DEBUG))
1752 report_command(state, next_state,
1753 cmd, buffer, size);
1754 continue; /* Ignore the command */
1755 }
1756 }
1757
1758 if (next_state != st_none) {
1759 state = next_state;
1760 ctx.state = state;
1761 }
1762
1763 if (parse_state_arg(&arg, sd->arg_type, buffer, size)) {
1764 if (GACOPYZ_DESC_LOG_MATCH(desc, SMI_LOG_ERR)) {
1765 gacopyz_log(SMI_LOG_ERR,
1766 _("argument parsing failed"));
1767 gacopyz_logdump(SMI_LOG_ERR,
1768 _("buffer"),
1769 buffer, size);
1770 }
1771 rc = MI_FAILURE;
1772 break;
1773 }
1774
1775 if (ctx.reply) {
1776 free(ctx.reply);
1777 ctx.reply = NULL;
1778 }
1779 if (sd->flags & CT_CLR)
1780 clear_macros(&ctx, sd->macro_ind + 1);
1781 ret = sd->fn(&ctx, &arg, &cmd);
1782 arg_free(&arg, sd->arg_type);
1783
1784 switch (ret) {
1785 case sret_noreply:
1786 break;
1787
1788 case sret_reply:
1789 rc = send_reply(&ctx, cmd);
1790 break;
1791
1792 case sret_reject:
1793 if (!(sd->flags & CT_IGN))
1794 state = st_helo;
1795 break;
1796
1797 case sret_fail:
1798 if (GACOPYZ_DESC_LOG_MATCH(desc, SMI_LOG_DEBUG))
1799 gacopyz_log(SMI_LOG_DEBUG,
1800 _("%s handler returned failure"),
1801 sd->name);
1802 break;
1803
1804 case sret_abort:
1805 if (GACOPYZ_DESC_LOG_MATCH(desc, SMI_LOG_DEBUG))
1806 gacopyz_log(SMI_LOG_DEBUG,
1807 _("%s handler returned abort"),
1808 sd->name);
1809 rc = MI_FAILURE;
1810 }
1811
1812 if (rc != MI_SUCCESS)
1813 break;
1814 }
1815
1816 if (GACOPYZ_DESC_LOG_MATCH(desc, SMI_LOG_DEBUG))
1817 gacopyz_log(SMI_LOG_DEBUG,
1818 _("end of context loop: %d"), rc);
1819
1820 if (rc != MI_SUCCESS && ST_IN_MAIL(state)
1821 && desc->xxfi_abort)
1822 desc->xxfi_abort(&ctx);
1823
1824 free(buffer);
1825 if (desc->xxfi_close)
1826 desc->xxfi_close(&ctx);
1827
1828 ctx_free(&ctx);
1829
1830 if (desc->xxfi_finish)
1831 desc->xxfi_finish();
1832
1833 return rc;
1834 }
1835
1836 int
gacopyz_handle_connection(gacopyz_conn_t conn)1837 gacopyz_handle_connection(gacopyz_conn_t conn)
1838 {
1839 milter_sockaddr_t addr;
1840 socklen_t addrlen = sizeof addr;
1841 int fd;
1842 int rc;
1843
1844 fd = accept(conn->sd, &addr.sa, &addrlen);
1845 if (fd == -1) {
1846 if (errno == EINTR)
1847 return MI_SUCCESS;
1848 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
1849 gacopyz_log(SMI_LOG_ERR,
1850 _("accept failed: %s"), strerror(errno));
1851 return MI_FAILURE;
1852 }
1853
1854 if (conn->desc.xxfi_accept
1855 && conn->desc.xxfi_accept(conn, fd, &addr, addrlen)) {
1856 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_DEBUG))
1857 gacopyz_log(SMI_LOG_DEBUG,
1858 _("connection refused by xxfi_accept"));
1859 close (fd);
1860 return MI_SUCCESS;
1861 }
1862
1863 if (!conn->foreground) {
1864 pid_t pid = fork();
1865 if (pid == -1) {
1866 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
1867 gacopyz_log(SMI_LOG_ERR,
1868 _("fork() failed: %s"),
1869 strerror(errno));
1870 return MI_FAILURE;
1871 }
1872 if (pid > 0) {
1873 gacopyz_register_child(conn, pid);
1874 close(fd);
1875 return MI_SUCCESS;
1876 }
1877 /* Child: */
1878 /* Reset signal handlers */
1879 signal(SIGCHLD, SIG_IGN);
1880 signal(SIGPIPE, SIG_DFL);
1881 signal(SIGTERM, SIG_DFL);
1882 signal(SIGHUP, SIG_DFL);
1883 /* Close listen descriptor */
1884 close(conn->sd);
1885 conn->sd = -1;
1886 }
1887
1888 switch (addr.sa.sa_family) {
1889 case AF_UNIX: {
1890 char *path;
1891
1892 if (addrlen == sizeof (addr.sa.sa_family))
1893 path = "[unnamed]";
1894 else
1895 path = addr.sunix.sun_path;
1896 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_INFO))
1897 gacopyz_log(SMI_LOG_INFO,
1898 _("connect from socket %s"),
1899 path);
1900 break;
1901 }
1902
1903 case AF_INET:
1904 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_INFO))
1905 gacopyz_log(SMI_LOG_INFO, _("connect from %s:%u"),
1906 inet_ntoa(addr.sin.sin_addr),
1907 (unsigned) ntohs(addr.sin.sin_port));
1908 break;
1909
1910 default:
1911 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_INFO))
1912 gacopyz_log(SMI_LOG_INFO,
1913 _("connection from unsupported family: %d"),
1914 addr.sa.sa_family);
1915 }
1916
1917 rc = gacopyz_context_loop(fd, &conn->desc, &addr, addrlen, NULL);
1918 close(fd);
1919
1920 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_INFO))
1921 gacopyz_log(SMI_LOG_INFO, _("finishing connection"));
1922
1923 if (!conn->foreground) {
1924 exit(0);
1925 }
1926 return rc;
1927 }
1928
1929 int
gacopyz_run(gacopyz_conn_t conn)1930 gacopyz_run(gacopyz_conn_t conn)
1931 {
1932 while (!conn->stop) {
1933 fd_set rfd;
1934 struct timeval timeout = conn->master_timeout;
1935 int rc;
1936
1937 gacopyz_cleanup_children(conn);
1938
1939 FD_ZERO(&rfd);
1940 FD_SET(conn->sd, &rfd);
1941 rc = select(conn->sd + 1, &rfd, NULL, NULL, &timeout);
1942 if (rc < 0) {
1943 if (errno == EINTR)
1944 continue;
1945 return MI_FAILURE;
1946 } else if (rc == 0) {
1947 if (conn->desc.xxfi_idle)
1948 conn->desc.xxfi_idle(conn);
1949 } else
1950 gacopyz_handle_connection(conn);
1951 }
1952 gacopyz_cleanup_conn(conn);
1953 return MI_SUCCESS;
1954 }
1955
1956 int
gacopyz_stop(gacopyz_conn_t conn)1957 gacopyz_stop(gacopyz_conn_t conn)
1958 {
1959 if (!conn)
1960 return MI_FAILURE;
1961 conn->stop = 1;
1962 return MI_SUCCESS;
1963 }
1964
1965 #define ISWS(c) ((c) == ' ' || (c) == '\t')
1966
1967 const char *
gacopyz_safe_header_value(const char * input,char ** poutput)1968 gacopyz_safe_header_value(const char *input, char **poutput)
1969 {
1970 char *output;
1971 const char *p;
1972 size_t len;
1973 size_t count;
1974 int tab, newline;
1975
1976 for (count = 0, p = input; *p && (p = strchr(p, '\n')); ) {
1977 if (p == input || (p[1] && !ISWS(p[1])))
1978 count++;
1979 p++;
1980 }
1981
1982 if (!count) {
1983 *poutput = NULL;
1984 return input;
1985 }
1986
1987 output = malloc(strlen(input) + count + 1);
1988 *poutput = output;
1989 if (!output)
1990 return NULL;
1991 newline = tab = 0;
1992 while (*input) {
1993 len = strcspn(input, "\n");
1994 if (len == 0 && input[1] == 0)
1995 break;
1996 if (newline)
1997 *output++ = '\n';
1998 else
1999 newline = 1;
2000 if (tab)
2001 *output++ = '\t';
2002 tab = !ISWS(input[len+1]);
2003 if (len) {
2004 memcpy(output, input, len);
2005 input += len;
2006 output += len;
2007 }
2008 if (*input)
2009 input++;
2010 }
2011 *output = 0;
2012 return *poutput;
2013 }
2014
2015 static int
gacopyz_header_command0(SMFICTX * ctx,unsigned char cmd,int idx,const char * headerf,const char * headerv)2016 gacopyz_header_command0(SMFICTX *ctx, unsigned char cmd,
2017 int idx, const char *headerf,
2018 const char *headerv)
2019 {
2020 char *buf;
2021 size_t bufsize;
2022 union header *hptr;
2023 char *cptr;
2024 size_t lenf, lenv;
2025 int rc;
2026
2027 if (!headerf || !*headerf || !headerv)
2028 return MI_FAILURE;
2029 lenf = strlen(headerf) + 1;
2030 lenv = strlen(headerv) + 1;
2031 bufsize = lenf + lenv;
2032 if (idx >= 0)
2033 bufsize += sizeof(gacopyz_uint32_t);
2034 buf = malloc(sizeof hptr->buf + bufsize);
2035 if (!buf)
2036 return MI_FAILURE;
2037 hptr = (union header *) buf;
2038 cptr = buf + sizeof hptr->buf;
2039 if (idx >= 0) {
2040 *(gacopyz_uint32_t*)cptr = htonl(idx);
2041 cptr += sizeof(gacopyz_uint32_t);
2042 }
2043 memcpy(cptr, headerf, lenf);
2044 cptr += lenf;
2045 memcpy(cptr, headerv, lenv);
2046 cptr += lenv;
2047
2048 TRACE(ctx, cmd, sizeof hptr->buf + bufsize, buf);
2049
2050 hptr->hdr.cmd = cmd;
2051 hptr->hdr.size = htonl(bufsize + 1);
2052 rc = ctx_write(ctx, buf, sizeof hptr->buf + bufsize);
2053 free(buf);
2054 return rc;
2055 }
2056
2057 static int
gacopyz_header_command(SMFICTX * ctx,unsigned char cmd,int idx,const char * headerf,const char * headerv)2058 gacopyz_header_command(SMFICTX *ctx, unsigned char cmd,
2059 int idx, const char *headerf,
2060 const char *headerv)
2061 {
2062 int rc;
2063 char *header_tmp;
2064
2065 if (!headerf || !*headerf || !headerv)
2066 return MI_FAILURE;
2067 headerv = gacopyz_safe_header_value(headerv, &header_tmp);
2068 if (!headerv)
2069 return MI_FAILURE;
2070 rc = gacopyz_header_command0(ctx, cmd, idx, headerf, headerv);
2071 free(header_tmp);
2072 return rc;
2073 }
2074
2075 static int
gacopyz_rcpt_command(SMFICTX * ctx,unsigned char cmd,const char * rcpt)2076 gacopyz_rcpt_command(SMFICTX *ctx, unsigned char cmd, const char *rcpt)
2077 {
2078 char *buf;
2079 size_t bufsize;
2080 union header *hptr;
2081 char *cptr;
2082 int rc;
2083
2084 if (!rcpt || !*rcpt)
2085 return MI_FAILURE;
2086 bufsize = strlen(rcpt) + 1;
2087 buf = malloc(sizeof hptr->buf + bufsize);
2088 if (!buf)
2089 return MI_FAILURE;
2090 hptr = (union header *) buf;
2091 cptr = buf + sizeof hptr->buf;
2092 strcpy(cptr, rcpt);
2093
2094 TRACE(ctx, cmd, bufsize, buf);
2095
2096 hptr->hdr.cmd = cmd;
2097 hptr->hdr.size = htonl(bufsize + 1);
2098 rc = ctx_write(ctx, buf, sizeof hptr->buf + bufsize);
2099 free(buf);
2100 return rc;
2101 }
2102
2103 static int
gacopyz_argn_command(SMFICTX * ctx,unsigned char cmd,int argc,char const ** argv)2104 gacopyz_argn_command(SMFICTX *ctx, unsigned char cmd, int argc, char const **argv)
2105 {
2106 char *buf;
2107 size_t bufsize;
2108 union header *hptr;
2109 char *cptr;
2110 int i, rc;
2111
2112 if (argc == 0)
2113 return MI_FAILURE;
2114 bufsize = 0;
2115 for (i = 0; i < argc; i++)
2116 bufsize += strlen(argv[i]) + 1;
2117 buf = malloc(sizeof hptr->buf + bufsize);
2118 if (!buf)
2119 return MI_FAILURE;
2120 hptr = (union header *) buf;
2121 cptr = buf + sizeof hptr->buf;
2122 for (i = 0; i < argc; i++) {
2123 char const *q;
2124 for (q = argv[i]; *cptr++ = *q++; );
2125 }
2126
2127 TRACE(ctx, cmd, sizeof hptr->buf + bufsize, buf);
2128
2129 hptr->hdr.cmd = cmd;
2130 hptr->hdr.size = htonl(bufsize + 1);
2131 rc = ctx_write(ctx, buf, sizeof hptr->buf + bufsize);
2132 free(buf);
2133 return rc;
2134 }
2135
2136 static int
ok_to_send(SMFICTX * ctx,int cmd)2137 ok_to_send(SMFICTX *ctx, int cmd)
2138 {
2139 if (!ctx)
2140 return 0;
2141 if (cmd && !(ctx->aflags & cmd))
2142 return 0;
2143 return ctx->state == st_endm;
2144 }
2145
2146 int
gacopyz_add_header(SMFICTX * ctx,const char * headerf,const char * headerv)2147 gacopyz_add_header(SMFICTX *ctx, const char *headerf, const char *headerv)
2148 {
2149 if (!ok_to_send(ctx, SMFIF_ADDHDRS))
2150 return MI_FAILURE;
2151 return gacopyz_header_command(ctx, SMFIR_ADDHEADER, -1,
2152 headerf, headerv);
2153 }
2154
2155 int
gacopyz_insert_header(SMFICTX * ctx,int index,const char * headerf,const char * headerv)2156 gacopyz_insert_header(SMFICTX *ctx, int index, const char *headerf,
2157 const char *headerv)
2158 {
2159 if (!ok_to_send(ctx, SMFIF_ADDHDRS) || index < 0)
2160 return MI_FAILURE;
2161 return gacopyz_header_command(ctx, SMFIR_INSHEADER, index,
2162 headerf, headerv);
2163 }
2164
2165 int
gacopyz_change_header(SMFICTX * ctx,int index,const char * headerf,const char * headerv)2166 gacopyz_change_header(SMFICTX *ctx, int index, const char *headerf,
2167 const char *headerv)
2168 {
2169 if (!ok_to_send(ctx, SMFIF_CHGHDRS) || index < 0)
2170 return MI_FAILURE;
2171 return gacopyz_header_command(ctx, SMFIR_CHGHEADER, index,
2172 headerf, headerv ? headerv : "");
2173 }
2174
2175 int
gacopyz_add_rcpt(SMFICTX * ctx,const char * rcpt)2176 gacopyz_add_rcpt(SMFICTX *ctx, const char *rcpt)
2177 {
2178 if (!ok_to_send(ctx, SMFIF_ADDRCPT))
2179 return MI_FAILURE;
2180 return gacopyz_rcpt_command(ctx, SMFIR_ADDRCPT, rcpt);
2181 }
2182
2183 int
gacopyz_del_rcpt(SMFICTX * ctx,const char * rcpt)2184 gacopyz_del_rcpt(SMFICTX *ctx, const char *rcpt)
2185 {
2186 if (!ok_to_send(ctx, SMFIF_DELRCPT))
2187 return MI_FAILURE;
2188 return gacopyz_rcpt_command(ctx, SMFIR_DELRCPT, rcpt);
2189 }
2190
2191 #define GACOPYZ_CHUNK_SIZE 65535
2192
2193 int
gacopyz_replace_body_fn(SMFICTX * ctx,void * bodyp,ssize_t (* cpf)(char *,void *,size_t))2194 gacopyz_replace_body_fn(SMFICTX *ctx,
2195 void *bodyp,
2196 ssize_t (*cpf)(char*,void*,size_t))
2197 {
2198 union header *hptr;
2199 char buf[sizeof hptr->buf + GACOPYZ_CHUNK_SIZE];
2200
2201 if (!bodyp || !cpf)
2202 return MI_FAILURE;
2203 if (!ok_to_send(ctx, SMFIF_CHGBODY))
2204 return MI_FAILURE;
2205
2206 hptr = (union header *)buf;
2207 hptr->hdr.cmd = SMFIR_REPLBODY;
2208 while (1) {
2209 ssize_t wrs = cpf(buf + sizeof hptr->buf, bodyp,
2210 GACOPYZ_CHUNK_SIZE);
2211 if (wrs == 0)
2212 break;
2213 if (wrs < 0)
2214 return MI_FAILURE;
2215 hptr->hdr.size = htonl(wrs + 1);
2216 TRACE(ctx, hptr->hdr.cmd, wrs, buf);
2217 if (ctx_write(ctx, buf, sizeof hptr->buf + wrs))
2218 return MI_FAILURE;
2219 }
2220 return MI_SUCCESS;
2221 }
2222
2223 struct bodyptr {
2224 const unsigned char *bodyp;
2225 size_t bodylen;
2226 };
2227
2228 static ssize_t
cpf_mem(char * dst,void * src,size_t size)2229 cpf_mem(char *dst, void *src, size_t size)
2230 {
2231 struct bodyptr *bp = src;
2232 if (size > bp->bodylen) {
2233 size = bp->bodylen;
2234 if (size == 0)
2235 return size;
2236 }
2237 memcpy(dst, bp->bodyp, size);
2238 bp->bodyp += size;
2239 bp->bodylen -= size;
2240 return size;
2241 }
2242
2243 int
gacopyz_replace_body(SMFICTX * ctx,const unsigned char * bodyp,size_t bodylen)2244 gacopyz_replace_body(SMFICTX *ctx, const unsigned char *bodyp, size_t bodylen)
2245 {
2246 struct bodyptr bp;
2247 bp.bodyp = bodyp;
2248 bp.bodylen = bodylen;
2249 return gacopyz_replace_body_fn(ctx, &bp, cpf_mem);
2250 }
2251
2252 struct bodyfd {
2253 int fd;
2254 };
2255
2256 static ssize_t
cpf_fd(char * dst,void * src,size_t size)2257 cpf_fd(char *dst, void *src, size_t size)
2258 {
2259 struct bodyfd *bp = src;
2260 return read(bp->fd, dst, size);
2261 }
2262
2263 int
gacopyz_replace_body_fd(SMFICTX * ctx,int fd)2264 gacopyz_replace_body_fd(SMFICTX *ctx, int fd)
2265 {
2266 struct bodyfd bp;
2267 bp.fd = fd;
2268 return gacopyz_replace_body_fn(ctx, &bp, cpf_fd);
2269 }
2270
2271 int
gacopyz_progress(SMFICTX * ctx)2272 gacopyz_progress(SMFICTX *ctx)
2273 {
2274 union header hdr;
2275 hdr.hdr.cmd = SMFIR_PROGRESS;
2276 hdr.hdr.size = htonl(1);
2277 TRACE(ctx, hdr.hdr.cmd, 0, NULL);
2278 return ctx_write(ctx, hdr.buf, sizeof hdr.buf);
2279 }
2280
2281 int
gacopyz_quarantine(SMFICTX * ctx,const char * reason)2282 gacopyz_quarantine(SMFICTX *ctx, const char *reason)
2283 {
2284 if (reason == NULL || *reason == '\0')
2285 return MI_FAILURE;
2286 if (!ok_to_send(ctx, SMFIF_QUARANTINE))
2287 return MI_FAILURE;
2288 return gacopyz_rcpt_command(ctx, SMFIR_QUARANTINE, reason);
2289 }
2290
2291 int
gacopyz_add_rcpt_par(SMFICTX * ctx,const char * rcpt,const char * args)2292 gacopyz_add_rcpt_par(SMFICTX *ctx, const char *rcpt, const char *args)
2293 {
2294 int n;
2295 char const *argv[2];
2296
2297 if (!rcpt || !*rcpt)
2298 return MI_FAILURE;
2299 if (!ok_to_send(ctx, SMFIF_ADDRCPT_PAR))
2300 return MI_FAILURE;
2301
2302 argv[0] = rcpt;
2303 if (args) {
2304 argv[1] = args;
2305 n = 2;
2306 } else
2307 n = 1;
2308 return gacopyz_argn_command(ctx, SMFIR_ADDRCPT_PAR, n, argv);
2309 }
2310
2311 int
gacopyz_chgfrom(SMFICTX * ctx,const char * from,const char * args)2312 gacopyz_chgfrom(SMFICTX *ctx, const char *from, const char *args)
2313 {
2314 int n;
2315 char const *argv[2];
2316
2317 if (!from || !*from)
2318 return MI_FAILURE;
2319 if (!ok_to_send(ctx, SMFIF_CHGFROM))
2320 return MI_FAILURE;
2321 argv[0] = from;
2322 if (args) {
2323 argv[1] = args;
2324 n = 2;
2325 } else
2326 n = 1;
2327 return gacopyz_argn_command(ctx, SMFIR_CHGFROM, n, argv);
2328 }
2329
2330 int
gacopyz_setsymlist(SMFICTX * ctx,enum gacopyz_stage ind,const char * macros)2331 gacopyz_setsymlist(SMFICTX *ctx, enum gacopyz_stage ind, const char *macros)
2332 {
2333 if (ind < 0 || ind >= gacopyz_stage_max)
2334 return MI_FAILURE;
2335 if (ctx->req_macros[ind])
2336 free(ctx->req_macros[ind]);
2337 if (macros == NULL)
2338 ctx->req_macros[ind] = NULL;
2339 else {
2340 ctx->req_macros[ind] = strdup(macros);
2341 if (!ctx->req_macros[ind])
2342 return MI_FAILURE;
2343 }
2344 return MI_SUCCESS;
2345 }
2346