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