1 /* This file is part of gacopyz.
2    Copyright (C) 2007-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 typedef struct gacopyz_macro_def *gacopyz_macro_def_t;
20 struct gacopyz_macro_def {
21 	char *name;
22 	char *value;
23 };
24 
25 #define _SRV_CONNECTED 0x0100
26 #define _SRV_READY     0x0200
27 #define _SRV_CLRDIS    0x0400
28 #define _SRV_SYS_MASK  0xff00
29 
30 struct gacopyz_srv {
31 	char *id;                      /* Server identifier (not used yet) */
32 	char *portspec;                /* Port spec */
33 	struct gacopyz_iod iod;
34 	int flags;
35 
36 	gacopyz_macro_def_t def;
37 	size_t ndefs;
38 	size_t maxdefs;
39 	struct sockaddr *source_addr;
40 	socklen_t source_addr_len;
41 
42 	int onerror;
43 	void (*memerror)(gacopyz_srv_t, const char *, unsigned int);
44 
45 	unsigned long version;
46 	unsigned long acts;
47 	unsigned long proto;
48 	char **req_macros[gacopyz_stage_max]; /* Required macros */
49 
50 	int (*cb_reply)(gacopyz_srv_t srv, int cmd, int rcmd, void *data);
51 	void *cb_data;
52 
53 	char *buf;
54 	size_t bufsize;
55 
56 	char *resp_ptr;
57 	size_t resp_size;
58 };
59 
60 void
default_memerror(gacopyz_srv_t srv,const char * file,unsigned int line)61 default_memerror(gacopyz_srv_t srv, const char *file, unsigned int line)
62 {
63 	gacopyz_io_log(&srv->iod, SMI_LOG_FATAL,
64 		       _("%s:%lu: not enough memory"),
65 		       file, line);
66 	abort();
67 }
68 
69 #define GACOPYZ_ASSERT(e)                                                     \
70  if (!(e)) {                                                                  \
71        fprintf(stderr, "%s:%d: GACOPYZ usage error: assertion failed: %s\n",  \
72                __FILE__, __LINE__, #e);                                       \
73        abort();                                                               \
74  }
75 
76 #define GACOPYZ_ALLOC(srv, expr) do                                           \
77   if (!(expr)) {                                                              \
78 	srv->memerror(srv, __FILE__, __LINE__);                               \
79 	if (!(expr))                                                          \
80 		default_memerror(srv, __FILE__, __LINE__);                    \
81   } while (0)
82 
83 static struct gacopyz_macro_def *
find_def(gacopyz_srv_t srv,const char * name,size_t len)84 find_def(gacopyz_srv_t srv, const char *name, size_t len)
85 {
86 	size_t i;
87 	for (i = 0; i < srv->ndefs; i++)
88 		if (strncmp(srv->def[i].name, name, len) == 0)
89 			return &srv->def[i];
90 	return NULL;
91 }
92 
93 static void
add_def(gacopyz_srv_t srv,const char * name,size_t len,const char * value)94 add_def(gacopyz_srv_t srv, const char *name, size_t len, const char *value)
95 {
96 	struct gacopyz_macro_def *def;
97 
98 	if (def = find_def(srv, name, len))
99 		free(def->value);
100 	else {
101 		if (srv->ndefs == srv->maxdefs) {
102 			size_t maxdefs = srv->maxdefs + 64;
103 			GACOPYZ_ALLOC(srv,
104 				      def = realloc(srv->def,
105 						    sizeof(srv->def[0]) * maxdefs));
106 			srv->maxdefs = maxdefs;
107 			srv->def = def;
108 		}
109 		def = srv->def + srv->ndefs++;
110 		GACOPYZ_ALLOC(srv, def->name = malloc(len + 1));
111 		memcpy(def->name, name, len);
112 		def->name[len] = 0;
113 	}
114 	GACOPYZ_ALLOC(srv, def->value = strdup(value));
115 }
116 
117 void
del_def(gacopyz_srv_t srv,const char * name,size_t len)118 del_def(gacopyz_srv_t srv, const char *name, size_t len)
119 {
120 	struct gacopyz_macro_def *def = find_def(srv, name, len);
121 
122 	if (!def)
123 		return;
124 
125 	free(def->name);
126 	free(def->value);
127 	srv->ndefs--;
128 	memmove(def, def + 1,
129 		 sizeof(srv->def[0]) *(srv->ndefs - (def - srv->def)));
130 }
131 
132 static void
srv_ensure_space(gacopyz_srv_t srv,size_t size)133 srv_ensure_space(gacopyz_srv_t srv, size_t size)
134 {
135 	if (size > srv->bufsize) {
136 		char *p;
137 		GACOPYZ_ALLOC(srv, p = realloc(srv->buf, size));
138 		srv->buf = p;
139 		srv->bufsize = size;
140 	}
141 }
142 
143 size_t
macro_size(gacopyz_srv_t srv)144 macro_size(gacopyz_srv_t srv)
145 {
146 	size_t i;
147 	size_t size = 0;
148 	struct gacopyz_macro_def *def;
149 
150 	for (i = 0, def = srv->def; i < srv->ndefs; i++, def++) {
151 		size_t len = strlen(def->name);
152 		size += len;
153 		if (len > 1 && def->name[0] != '{')
154 			size += 2; /* for {} */
155 		size += 1 + strlen(def->value) + 1;
156 	}
157 	return size;
158 }
159 
160 void
srv_format_macros(gacopyz_srv_t srv,unsigned char cmd,size_t * psize)161 srv_format_macros(gacopyz_srv_t srv, unsigned char cmd, size_t *psize)
162 {
163 	size_t i;
164 	char *p;
165 	size_t size = macro_size(srv) + 1;
166 	struct gacopyz_macro_def *def;
167 
168 	srv_ensure_space(srv, size);
169 
170 	p = srv->buf;
171 	*p++ = cmd;
172 	for (i = 0, def = srv->def; i < srv->ndefs; i++, def++) {
173 		size_t len = strlen(def->name);
174 		if (len > 1 && def->name[0] != '{') {
175 			*p++ = '{';
176 			memcpy(p, def->name, len);
177 			p += len;
178 			*p++ = '}';
179 			*p++ = 0;
180 		} else {
181 			len++;
182 			memcpy(p, def->name, len);
183 			p += len;
184 		}
185 		len = strlen(def->value) + 1;
186 		memcpy(p, def->value, len);
187 		p += len;
188 	}
189 	*psize = size;
190 }
191 
192 
193 
194 #define GETNAMELEN(name, len)                          \
195 	len = strlen(name);                            \
196 	if (name[0] == '{' && name[len-1] == '}') {    \
197 		name++;                                \
198 		len -= 2;                              \
199 	}
200 
201 int
gacopyz_srv_find_macro(gacopyz_srv_t srv,const char * name,const char ** pval)202 gacopyz_srv_find_macro(gacopyz_srv_t srv, const char *name, const char **pval)
203 {
204 	struct gacopyz_macro_def *def;
205 	size_t len;
206 
207 	GACOPYZ_ASSERT(srv);
208 
209 	GETNAMELEN(name, len);
210 	def = find_def(srv, name, len);
211 	if (def) {
212 		*pval = def->value;
213 		return MI_SUCCESS;
214 	}
215 	return MI_FAILURE;
216 }
217 
218 void
gacopyz_srv_define_macro(gacopyz_srv_t srv,const char * name,const char * value)219 gacopyz_srv_define_macro(gacopyz_srv_t srv,
220 			  const char *name, const char *value)
221 {
222 	size_t len;
223 
224 	GACOPYZ_ASSERT(srv);
225 
226 	GETNAMELEN(name, len);
227 
228 	add_def(srv, name, len, value);
229 }
230 
231 void
gacopyz_srv_del_macro(gacopyz_srv_t srv,const char * name)232 gacopyz_srv_del_macro(gacopyz_srv_t srv, const char *name)
233 {
234 	size_t len;
235 
236 	GACOPYZ_ASSERT(srv);
237 
238 	GETNAMELEN(name, len);
239 	del_def(srv, name, len);
240 }
241 
242 void
gacopyz_srv_clear_macros(gacopyz_srv_t srv)243 gacopyz_srv_clear_macros(gacopyz_srv_t srv)
244 {
245 	size_t i;
246 
247 	GACOPYZ_ASSERT(srv);
248 
249 	for (i = 0; i < srv->ndefs; i++) {
250 		free(srv->def[i].name);
251 		free(srv->def[i].value);
252 	}
253 	srv->ndefs = 0;
254 }
255 
256 void
gacopyz_srv_clear_macros_pred(gacopyz_srv_t srv,int (* pred)(const char *,void *),void * data)257 gacopyz_srv_clear_macros_pred(gacopyz_srv_t srv,
258 			      int (*pred)(const char*, void*),
259 			      void *data)
260 {
261 	size_t i, j;
262 
263 	GACOPYZ_ASSERT(srv);
264 
265 	for (i = j = 0; i < srv->ndefs; i++) {
266 		if (pred(srv->def[i].name, data)) {
267 			free(srv->def[i].name);
268 			free(srv->def[i].value);
269 		} else
270 			srv->def[j++] = srv->def[i];
271 	}
272 	srv->ndefs = j;
273 }
274 
275 void
gacopyz_srv_iterate_macros(gacopyz_srv_t srv,int (* func)(const char * name,const char * value,void * data),void * data)276 gacopyz_srv_iterate_macros(gacopyz_srv_t srv,
277 			    int (*func)(const char *name, const char *value,
278 					 void *data),
279 			    void *data)
280 {
281 	size_t i;
282 
283 	GACOPYZ_ASSERT(srv);
284 
285 	for (i = 0; i < srv->ndefs; i++)
286 		if (func(srv->def[i].name, srv->def[i].value, data))
287 			break;
288 }
289 
290 void
gacopyz_srv_count_macros(gacopyz_srv_t srv,size_t * count)291 gacopyz_srv_count_macros(gacopyz_srv_t srv, size_t *count)
292 {
293 	GACOPYZ_ASSERT(srv);
294 	*count = srv->ndefs;
295 }
296 
297 
298 const char **
gacopyz_srv_get_required_macros(gacopyz_srv_t srv,enum gacopyz_stage stage)299 gacopyz_srv_get_required_macros(gacopyz_srv_t srv, enum gacopyz_stage stage)
300 {
301 	return (const char **) srv->req_macros[stage];
302 }
303 
304 
305 const char *
gacopyz_srv_get_id(gacopyz_srv_t srv)306 gacopyz_srv_get_id(gacopyz_srv_t srv)
307 {
308 	return srv->id;
309 }
310 
311 const char *
gacopyz_srv_get_portspec(gacopyz_srv_t srv)312 gacopyz_srv_get_portspec(gacopyz_srv_t srv)
313 {
314 	return srv->portspec;
315 }
316 
317 int
gacopyz_srv_get_flags(gacopyz_srv_t srv)318 gacopyz_srv_get_flags(gacopyz_srv_t srv)
319 {
320 	return srv->flags & ~_SRV_SYS_MASK;
321 }
322 
323 int
gacopyz_srv_flags(gacopyz_srv_t srv,int flags,enum gacopyz_flag_op op)324 gacopyz_srv_flags(gacopyz_srv_t srv, int flags, enum gacopyz_flag_op op)
325 {
326 	if (flags & _SRV_SYS_MASK)
327 		return EINVAL;
328 	switch (op) {
329 	case gacopyz_flag_rpl:
330 		srv->flags = (srv->flags & _SRV_SYS_MASK) | flags;
331 		break;
332 
333 	case gacopyz_flag_set:
334 		srv->flags |= flags;
335 		break;
336 
337 	case gacopyz_flag_clr:
338 		srv->flags &= ~flags;
339 	}
340 	/* Whether set or cleared, the GACOPYZ_SRV_DISABLED bit
341 	   implies clearing _SRV_CLRDIS. */
342 	if (flags & GACOPYZ_SRV_DISABLED)
343 		flags &= ~_SRV_CLRDIS;
344 	return 0;
345 }
346 
347 int
gacopyz_srv_get_logmask(gacopyz_srv_t srv)348 gacopyz_srv_get_logmask(gacopyz_srv_t srv)
349 {
350 	return srv->iod.logmask;
351 }
352 
353 int
gacopyz_srv_get_fd(gacopyz_srv_t srv)354 gacopyz_srv_get_fd(gacopyz_srv_t srv)
355 {
356 	return srv->iod.sd;
357 }
358 
359 int
gacopyz_srv_get_onerr(gacopyz_srv_t srv)360 gacopyz_srv_get_onerr(gacopyz_srv_t srv)
361 {
362 	return srv->onerror;
363 }
364 
365 
366 struct timeval default_gacopyz_timeout[GACOPYZ_TO_COUNT] = {
367   { GACOPYZ_WRITE_TIMEOUT, 0 },
368   { GACOPYZ_READ_TIMEOUT, 0 },
369   { GACOPYZ_EOM_TIMEOUT, 0 },
370   { GACOPYZ_CONNECT_TIMEOUT, 0 }
371 };
372 
373 int
gacopyz_srv_create(gacopyz_srv_t * p,const char * name,const char * portspec,unsigned logmask)374 gacopyz_srv_create(gacopyz_srv_t *p, const char *name,
375 		   const char *portspec, unsigned logmask)
376 {
377 	gacopyz_srv_t srv = calloc(1, sizeof(*srv));
378 
379 	if (!srv)
380 		return MI_FAILURE;
381 	srv->id = strdup(name);
382 	srv->portspec = strdup(portspec);
383 	srv->iod.sd = -1;
384 	srv->iod.logmask = logmask;
385 	memcpy(srv->iod.timeout, default_gacopyz_timeout,
386 	       sizeof srv->iod.timeout);
387 
388 	srv->flags = 0;
389 	srv->onerror = SMFIR_TEMPFAIL;
390 	srv->version = SMFI_VERSION;
391 	srv->proto = SMFI_DEFAULT_PROT;
392 	srv->acts = SMFI_DEFAULT_ACTS;
393 	srv->memerror = default_memerror;
394 	*p = srv;
395 	return MI_SUCCESS;
396 }
397 
398 static int
parse_X_spec(const char * spec,char * xspec,char ** id,char ** port,struct timeval to[],int * onerr,int debug)399 parse_X_spec(const char *spec, char *xspec, char **id, char **port,
400 	     struct timeval to[], int *onerr, int debug)
401 {
402 	char *p, *q;
403 	int havef = 0, havet = 0;
404 
405 	p = strtok(xspec, ",");
406 	if (!p) {
407 		if (debug)
408 			gacopyz_log(SMI_LOG_DEBUG,
409 				    _("%s: not a valid X spec"),
410 				    spec);
411 		return MI_FAILURE;
412 	}
413 	if (p[1] != '=') {
414 		*id = p;
415 		p = strtok(NULL, ",");
416 		if (!p) {
417 			if (debug)
418 				gacopyz_log(SMI_LOG_DEBUG,
419 					       _("%s: not a valid X spec"),
420 					       spec);
421 			return MI_FAILURE;
422 		}
423 	}
424 
425 	do {
426 		while (*p && isspace(*p))
427 			p++;
428 		if (!*p)
429 			break;
430 		if (p[1] != '=') {
431 			if (debug)
432 				gacopyz_log(SMI_LOG_DEBUG,
433 					       _("%s: not a valid X spec, "
434 					         "missing = near %s"),
435 					       spec, p);
436 			return MI_FAILURE;
437 		}
438 
439 		switch (p[0]) {
440 		case 'S':
441 			if (*port) {
442 				if (debug)
443 					gacopyz_log(SMI_LOG_DEBUG,
444 						       _("%s: not a valid X spec, "
445 						         "duplicate field %s"),
446 						       spec, p);
447 				return MI_FAILURE;
448 			}
449 			*port = p + 2;
450 			break;
451 
452 		case 'F':
453 			if (havef) {
454 				if (debug)
455 					gacopyz_log(SMI_LOG_DEBUG,
456 						       _("%s: not a valid X spec, "
457 						         "duplicate field %s"),
458 						       spec, p);
459 				return MI_FAILURE;
460 			}
461 			havef = 1;
462 			if (strlen(p) != 3) {
463 				if (debug)
464 					gacopyz_log(SMI_LOG_DEBUG,
465 						       _("%s: not a valid X spec, "
466 						         "invalid field %s"),
467 						       spec, p);
468 				return MI_FAILURE;
469 			}
470 			switch (p[2]) {
471 			case 'R':
472 				*onerr = SMFIR_REJECT;
473 				break;
474 
475 			case 'T':
476 				*onerr = SMFIR_TEMPFAIL;
477 				break;
478 
479 			default:
480 				if (debug)
481 					gacopyz_log(SMI_LOG_DEBUG,
482 						       _("%s: not a valid X spec, "
483 						         "invalid field %s"),
484 						       spec, p);
485 				return MI_FAILURE;
486 			}
487 			break;
488 
489 		case 'T':
490 			if (havet) {
491 				if (debug)
492 					gacopyz_log(SMI_LOG_DEBUG,
493 						       _("%s: not a valid X spec, "
494 						         "duplicate field %s"),
495 						       spec, p);
496 				return MI_FAILURE;
497 			}
498 			havet = 1;
499 
500 			for (q = p + 2; *q; q++) {
501 				int n;
502 				unsigned long t;
503 				char *ep;
504 
505 				switch (*q) {
506 				case 'C':
507 					n = GACOPYZ_TO_CONNECT;
508 					break;
509 
510 				case 'S':
511 					n = GACOPYZ_TO_WRITE;
512 					break;
513 
514 				case 'R':
515 					n = GACOPYZ_TO_READ;
516 					break;
517 
518 				case 'E':
519 					n = GACOPYZ_TO_EOM;
520 					break;
521 
522 				default:
523 					if (debug)
524 						gacopyz_log(SMI_LOG_DEBUG,
525 					           _("%s: not a valid X spec, "
526 					             "invalid T field near %s"),
527 							       spec, q);
528 					return MI_FAILURE;
529 				}
530 				if (*++q != ':') {
531 					if (debug)
532 						gacopyz_log(SMI_LOG_DEBUG,
533 					           _("%s: not a valid X spec, "
534 					             "invalid T field near %s"),
535 							       spec, q);
536 					return MI_FAILURE;
537 				}
538 				t = strtoul(++q, &ep, 0);
539 				switch (*ep) {
540 				case 0:
541 				case ';':
542 					break;
543 				case 's':
544 					ep++;
545 					break;
546 				case 'm':
547 					t *= 60;
548 					ep++;
549 					break;
550 				default:
551 					if (debug)
552 						gacopyz_log(SMI_LOG_DEBUG,
553 					           _("%s: not a valid X spec, "
554 					             "invalid T field near %s"),
555 							       spec, q);
556 					return MI_FAILURE;
557 				}
558 
559 				to[n].tv_sec = t;
560 				to[n].tv_usec = 0;
561 
562 				q = ep;
563 
564 				if (*q == 0)
565 					break;
566 				else if (*q != ';') {
567 					if (debug)
568 						gacopyz_log(SMI_LOG_DEBUG,
569 					           _("%s: not a valid X spec, "
570 					             "invalid delimiter near %s"),
571 							       spec, q);
572 					return MI_FAILURE;
573 				}
574 			}
575 		}
576 	} while (p = strtok(NULL, ","));
577 	return MI_SUCCESS;
578 }
579 
580 int
gacopyz_srv_create_X(gacopyz_srv_t * p,const char * spec,unsigned logmask)581 gacopyz_srv_create_X(gacopyz_srv_t *p, const char *spec, unsigned logmask)
582 {
583 	int rc;
584 	char *xspec = strdup(spec);
585 
586 	char *id = "default";
587 	char *port = NULL;
588 	struct timeval to[GACOPYZ_TO_COUNT];
589 	int onerr = SMFIR_CONTINUE;
590 
591 	memcpy(to, default_gacopyz_timeout, sizeof to);
592 	if (parse_X_spec(spec, xspec, &id, &port, to, &onerr,
593 			logmask & SMI_LOG_MASK(SMI_LOG_DEBUG)) == MI_FAILURE) {
594 		free(xspec);
595 		return MI_FAILURE;
596 	}
597 
598 	if (!port) {
599 		if (logmask & SMI_LOG_MASK(SMI_LOG_DEBUG))
600 			gacopyz_log(SMI_LOG_DEBUG,
601 				       _("%s: not a valid X spec, "
602 				         "missing port"),
603 				       spec);
604 		free(xspec);
605 		return MI_FAILURE;
606 	}
607 
608 	rc = gacopyz_srv_create(p, id, port, logmask);
609 	free(xspec);
610 	if (rc)
611 		return MI_FAILURE;
612 	gacopyz_srv_set_all_timeouts(*p, to);
613 	gacopyz_srv_onerror(*p, onerr);
614 	return MI_SUCCESS;
615 }
616 
617 int
gacopyz_srv_onerror(gacopyz_srv_t srv,int code)618 gacopyz_srv_onerror(gacopyz_srv_t srv, int code)
619 {
620 	GACOPYZ_ASSERT(srv);
621 
622 	switch (code) {
623 	case SMFIR_CONTINUE:
624 	case SMFIR_DISCARD:
625 	case SMFIR_REJECT:
626 	case SMFIR_TEMPFAIL:
627 	case SMFIR_SHUTDOWN:
628 		srv->onerror = code;
629 		return MI_SUCCESS;
630 	}
631 	return MI_FAILURE;
632 }
633 
634 void
gacopyz_srv_set_logmask(gacopyz_srv_t srv,int logmask)635 gacopyz_srv_set_logmask(gacopyz_srv_t srv, int logmask)
636 {
637 	GACOPYZ_ASSERT(srv);
638 	srv->iod.logmask = logmask;
639 }
640 
641 void
gacopyz_srv_set_callback(gacopyz_srv_t srv,int (* cb)(gacopyz_srv_t,int,int,void *))642 gacopyz_srv_set_callback(gacopyz_srv_t srv,
643 			 int (*cb)(gacopyz_srv_t, int, int, void *))
644 {
645 	GACOPYZ_ASSERT(srv);
646 	srv->cb_reply = cb;
647 	return;
648 }
649 
650 void
gacopyz_srv_set_callback_data(gacopyz_srv_t srv,void * data)651 gacopyz_srv_set_callback_data(gacopyz_srv_t srv, void *data)
652 {
653 	GACOPYZ_ASSERT(srv);
654 	srv->cb_data = data;
655 	return;
656 }
657 
658 void
gacopyz_srv_set_memerror(gacopyz_srv_t srv,void (* memerror)(gacopyz_srv_t,const char *,unsigned int))659 gacopyz_srv_set_memerror(gacopyz_srv_t srv,
660 			 void (*memerror)(gacopyz_srv_t, const char *,
661 					  unsigned int))
662 {
663 	GACOPYZ_ASSERT(srv);
664 	srv->memerror = memerror;
665 }
666 
667 void
gacopyz_srv_set_all_timeouts(gacopyz_srv_t srv,struct timeval * tvp)668 gacopyz_srv_set_all_timeouts(gacopyz_srv_t srv, struct timeval *tvp)
669 {
670 	GACOPYZ_ASSERT(srv);
671 	memcpy(srv->iod.timeout, tvp, sizeof srv->iod.timeout);
672 }
673 
674 int
gacopyz_srv_set_timeout(gacopyz_srv_t srv,unsigned n,struct timeval * tvp)675 gacopyz_srv_set_timeout(gacopyz_srv_t srv, unsigned n, struct timeval *tvp)
676 {
677 	GACOPYZ_ASSERT(srv);
678 
679 	if (n >= GACOPYZ_TO_COUNT)
680 		return MI_FAILURE;
681 	srv->iod.timeout[n] = *tvp;
682 	return MI_SUCCESS;
683 }
684 
685 int
gacopyz_srv_set_timeout_sec(gacopyz_srv_t srv,unsigned n,time_t t)686 gacopyz_srv_set_timeout_sec(gacopyz_srv_t srv, unsigned n, time_t t)
687 {
688 	struct timeval tv;
689 	tv.tv_usec = 0;
690 	tv.tv_sec = t;
691 	return gacopyz_srv_set_timeout(srv, n, &tv);
692 }
693 
694 int
gacopyz_srv_set_source(gacopyz_srv_t srv,struct sockaddr * sa,socklen_t len)695 gacopyz_srv_set_source(gacopyz_srv_t srv, struct sockaddr *sa, socklen_t len)
696 {
697 	GACOPYZ_ASSERT(srv);
698 
699 	srv->source_addr = malloc(len);
700 	if (!srv->source_addr)
701 		return MI_FAILURE;
702 	memcpy (srv->source_addr, sa, len);
703 	srv->source_addr_len = len;
704 	return MI_SUCCESS;
705 }
706 
707 int
gacopyz_srv_set_version(gacopyz_srv_t srv,unsigned long version)708 gacopyz_srv_set_version(gacopyz_srv_t srv, unsigned long version)
709 {
710 	GACOPYZ_ASSERT(srv);
711 
712 	srv->version = version;
713 	return MI_SUCCESS;
714 }
715 
716 int
gacopyz_srv_set_protocol(gacopyz_srv_t srv,unsigned long proto)717 gacopyz_srv_set_protocol(gacopyz_srv_t srv, unsigned long proto)
718 {
719 	GACOPYZ_ASSERT(srv);
720 
721 	srv->proto = proto;
722 	return MI_SUCCESS;
723 }
724 
725 int
gacopyz_srv_set_actions(gacopyz_srv_t srv,unsigned long acts)726 gacopyz_srv_set_actions(gacopyz_srv_t srv, unsigned long acts)
727 {
728 	GACOPYZ_ASSERT(srv);
729 
730 	srv->acts = acts;
731 	return MI_SUCCESS;
732 }
733 
734 void
gacopyz_srv_destroy(gacopyz_srv_t * p)735 gacopyz_srv_destroy(gacopyz_srv_t *p)
736 {
737 	int i;
738 
739 	gacopyz_srv_t srv = *p;
740 	gacopyz_srv_clear_macros(srv);
741 	free(srv->portspec);
742 	free(srv->id);
743 	free(srv->def);
744 	free(srv->source_addr);
745 	for (i = 0; i < gacopyz_stage_max; i++)
746 		if (srv->req_macros[i]) {
747 			int j;
748 
749 			for (j = 0; srv->req_macros[i][j]; j++)
750 				free(srv->req_macros[i][j]);
751 			free(srv->req_macros[i]);
752 		}
753 	free(srv->buf);
754 	free(srv);
755 	*p = 0;
756 }
757 
758 static int
srv_connect(gacopyz_srv_t srv,char * proto,char * port,char * path)759 srv_connect(gacopyz_srv_t srv, char *proto, char *port, char *path)
760 {
761 	milter_sockaddr_t addr;
762 	int socklen;
763 	int fd = -1;
764 	struct timeval start, connect_tv;
765 
766 	if (!proto
767 	    || strcmp(proto, "unix") == 0 || strcmp(proto, "local") == 0) {
768 		struct stat st;
769 
770 		if (port) {
771 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
772 					_("invalid connection type: %s; "
773 					  "port is meaningless for UNIX sockets"),
774 				       srv->portspec);
775 			return -1;
776 		}
777 
778 		if (strlen(path) >= sizeof addr.sunix.sun_path) {
779 			errno = EINVAL;
780 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
781 				       _("%s: UNIX socket name too long"),
782 				       path);
783 			return -1;
784 		}
785 
786 		addr.sa.sa_family = PF_UNIX;
787 		socklen = sizeof(addr.sunix);
788 		strcpy(addr.sunix.sun_path, path);
789 
790 		if (stat(path, &st)) {
791 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
792 				       _("%s: cannot stat socket: %s"),
793 				       path, strerror(errno));
794 			return -1;
795 		} else {
796 			/* FIXME: Check permissions? */
797 			if (!S_ISSOCK(st.st_mode)) {
798 				gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
799 					       _("%s: not a socket"),
800 						path);
801 				return -1;
802 			}
803 		}
804 
805 	} else if (strcmp(proto, "inet") == 0) {
806 		short pnum;
807 		long num;
808 		char *p;
809 
810 		addr.sa.sa_family = PF_INET;
811 		socklen = sizeof(addr.sin);
812 
813 		if (!port) {
814 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
815 				       _("invalid connection type: %s; "
816 				         "missing port number"),
817 				       srv->portspec);
818 			return -1;
819 		}
820 
821 		num = pnum = strtol(port, &p, 0);
822 		if (*p == 0) {
823 			if (num != pnum) {
824 				gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
825 					       _("invalid connection type: %s; "
826 					         "bad port number"),
827 					       srv->portspec);
828 				return -1;
829 			}
830 			pnum = htons(pnum);
831 		} else {
832 			struct servent *sp = getservbyname(port, "tcp");
833 			if (!sp) {
834 				gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
835 					       _("invalid connection type: %s; "
836 					         "unknown port name"),
837 					       srv->portspec);
838 				return -1;
839 			}
840 			pnum = sp->s_port;
841 		}
842 
843 		if (!path)
844 			addr.sin.sin_addr.s_addr = INADDR_ANY;
845 		else {
846 			struct hostent *hp = gethostbyname(path);
847 			if (!hp) {
848 				gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
849 					       _("unknown host name %s"),
850 					       path);
851 				return -1;
852 			}
853 			addr.sa.sa_family = hp->h_addrtype;
854 			switch (hp->h_addrtype) {
855 			case AF_INET:
856 				memmove(&addr.sin.sin_addr, hp->h_addr, 4);
857 				addr.sin.sin_port = pnum;
858 				break;
859 
860 			default:
861 				gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
862 					       _("invalid connection type: %s; "
863 					         "unsupported address family"),
864 					       srv->portspec);
865 				return -1;
866 			}
867 		}
868 #ifdef GACOPYZ_IPV6
869 	} else if (strcmp(proto, "inet6") == 0) {
870 		int rc;
871 		struct addrinfo hints;
872 		struct addrinfo *res;
873 
874 		memset(&hints, 0, sizeof(hints));
875 		hints.ai_family = AF_INET6;
876 		hints.ai_socktype = SOCK_STREAM;
877 		if (!path)
878 			hints.ai_flags |= AI_PASSIVE;
879 		rc = getaddrinfo(path, port, &hints, &res);
880 
881 		switch (rc) {
882 		case 0:
883 			break;
884 		case EAI_SYSTEM:
885 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
886 				       _("%s:%s: cannot parse address: %s"),
887 				       path, port, strerror(errno));
888 			return -1;
889 
890 		case EAI_BADFLAGS:
891 		case EAI_SOCKTYPE:
892 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
893 				       _("%s:%d: internal error "
894 					 "converting %s:%s"),
895 				       __FILE__, __LINE__, path, port);
896 			return -1;
897 
898 		case EAI_MEMORY:
899 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
900 				       "not enogh memory");
901 			return -1;
902 
903 		default:
904 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
905 				       "%s:%s: %s",
906 				       path, port, gai_strerror(rc));
907 			return -1;
908 		}
909 
910 		if (res->ai_addrlen > sizeof(addr)) {
911 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
912 				       _("%s:%s: address length too big (%lu)"),
913 				       path, port,
914 				       (unsigned long) res->ai_addrlen);
915 			freeaddrinfo(res);
916 			return -1;
917 		}
918 
919 		if (res->ai_next) {
920 			char host[NI_MAXHOST], serv[NI_MAXSERV];
921 			rc = getnameinfo(res->ai_addr, res->ai_addrlen,
922 					 host, sizeof host,
923 					 serv, sizeof serv,
924 					 NI_NUMERICHOST|NI_NUMERICSERV);
925 
926 			gacopyz_io_log(&srv->iod, SMI_LOG_WARN,
927 				       _("%s:%s resolves to several addresses; "
928 					 "using %s:%s"),
929 				       path, port, host, serv);
930 		}
931 		memcpy(&addr, res->ai_addr, res->ai_addrlen);
932 		socklen = sizeof(addr.sin6);
933 		freeaddrinfo(res);
934 #endif
935 	} else {
936 		gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
937 			       _("unsupported protocol: %s"),
938 			       proto);
939 		return -1;
940 	}
941 
942 	fd = socket(addr.sa.sa_family, SOCK_STREAM, 0);
943 	if (fd == -1) {
944 		gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
945 			       _("unable to create new socket: %s"),
946 			       strerror(errno));
947 		return -1;
948 	}
949 
950 	if (srv->source_addr && addr.sa.sa_family != PF_UNIX) {
951 		if (bind(fd, srv->source_addr, srv->source_addr_len) < 0) {
952 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
953 				       _("cannot bind to local address: %s"),
954 				       strerror(errno));
955 			close(fd);
956 			return -1;
957 		}
958 	}
959 
960 	connect_tv = srv->iod.timeout[GACOPYZ_TO_CONNECT];
961 	gettimeofday(&start, NULL);
962 	for (;;) {
963 		int rc = connect(fd, &addr.sa, socklen);
964 		if (rc == 0)
965 			break;
966 		else if (errno == ECONNREFUSED) {
967 			struct timeval tv, now;
968 			fd_set rset, wset, xset;
969 
970 			gettimeofday(&now, NULL);
971 			timersub(&now, &start, &tv);
972 
973 			if (timercmp(&tv, &connect_tv, < )) {
974 				FD_ZERO(&rset);
975 				FD_SET(fd, &rset);
976 				FD_ZERO(&wset);
977 				FD_SET(fd, &wset);
978 				FD_ZERO(&xset);
979 				FD_SET(fd, &xset);
980 				select(fd + 1, &rset, &wset, &xset, &tv);
981 				continue;
982 			}
983 		}
984 
985 		gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
986 			       _("cannot connect to %s: %s"),
987 			       srv->portspec, strerror(errno));
988 		close(fd);
989 		return -1;
990 	}
991 
992 	return fd;
993 }
994 
995 int
gacopyz_srv_open(gacopyz_srv_t srv)996 gacopyz_srv_open(gacopyz_srv_t srv)
997 {
998 	char *proto;
999 	char *port;
1000 	char *path;
1001 
1002 	GACOPYZ_ASSERT(srv);
1003 
1004 	gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1005 		       _("connecting to %s"), srv->portspec);
1006 	if (srv->flags & _SRV_CONNECTED) {
1007 		gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1008 			       _("%s: already connected, disconnect first"),
1009 			       srv->id);
1010 		return MI_FAILURE;
1011 	}
1012 	GACOPYZ_ALLOC(srv,
1013 		      gacopyz_parse_connection(srv->portspec,
1014 					       &proto, &port, &path)==0);
1015 
1016 	srv->iod.sd = srv_connect(srv, proto, port, path);
1017 	free(proto);
1018 	free(port);
1019 	free(path);
1020 	if (srv->iod.sd == -1)
1021 		return MI_FAILURE;
1022 	srv->flags |= _SRV_CONNECTED;
1023 	return MI_SUCCESS;
1024 }
1025 
1026 #define ISWS(c) ((c) == ' ' || (c) == '\t')
1027 
1028 static void
parse_macros(gacopyz_srv_t srv,enum gacopyz_stage stage,char * buf)1029 parse_macros(gacopyz_srv_t srv, enum gacopyz_stage stage, char *buf)
1030 {
1031 	char *p;
1032 	char **argv;
1033 	size_t count;
1034 
1035 	count = 0;
1036 	for (p = buf; *p;) {
1037 		while (*p && ISWS(*p))
1038 			p++;
1039 		if (*p) {
1040 			count++;
1041 			while (*p && !ISWS(*p))
1042 				p++;
1043 		}
1044 	}
1045 	if (count == 0)
1046 		return;
1047 	GACOPYZ_ALLOC(srv, argv = calloc(count + 1, sizeof(argv[0])));
1048 	count = 0;
1049 	for (p = buf; *p;) {
1050 		while (*p && ISWS(*p))
1051 			p++;
1052 		if (*p) {
1053 			size_t i;
1054 			char *str = p;
1055 
1056 			for (i = 0; *p && !ISWS(*p); p++, i++)
1057 				;
1058 			if (i > 0) {
1059 				char *newstr;
1060 
1061 				if (*str == '{' && str[i-1] == '}') {
1062 					str++;
1063 					i -= 2;
1064 				}
1065 				GACOPYZ_ALLOC(srv, newstr = malloc(i + 1));
1066 				memcpy(newstr, str, i);
1067 				newstr[i] = 0;
1068 				argv[count++] = newstr;
1069 			}
1070 		}
1071 	}
1072 	argv[count] = NULL;
1073 	srv->req_macros[stage] = argv;
1074 }
1075 
1076 
1077 static void
read_macros(gacopyz_srv_t srv)1078 read_macros(gacopyz_srv_t srv)
1079 {
1080 	char *buf = srv->buf + 3 * sizeof(gacopyz_uint32_t);
1081 	size_t size = srv->bufsize - 3 * sizeof(gacopyz_uint32_t);
1082 
1083 	while (size > sizeof(gacopyz_uint32_t)) {
1084 		gacopyz_uint32_t v;
1085 		unsigned n;
1086 		size_t len;
1087 
1088 		memcpy(&v, buf, sizeof(gacopyz_uint32_t));
1089 		n = ntohl(v);
1090 		if (n >= gacopyz_stage_max) {
1091 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
1092 				       _("received invalid stage number"));
1093 			break;
1094 		}
1095 		buf += sizeof(gacopyz_uint32_t);
1096 		size -= sizeof(gacopyz_uint32_t);
1097 		if (size == 0)
1098 			break;
1099 		len = strlen(buf) + 1;
1100 		if (len > size) {
1101 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
1102 				       _("invalid macro list"));
1103 			break;
1104 		}
1105 		gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1106 			       _("macros for stage \"%s\" (%d): %s"),
1107 			       gacopyz_stage_name[n], n, buf);
1108 		parse_macros(srv, n, buf);
1109 		buf += len;
1110 		size -= len;
1111 	}
1112 }
1113 
1114 int
gacopyz_srv_negotiate(gacopyz_srv_t srv)1115 gacopyz_srv_negotiate(gacopyz_srv_t srv)
1116 {
1117 	gacopyz_uint32_t ibuf[3];
1118 	size_t size;
1119 	unsigned char cmd;
1120 	unsigned long milter_version, milter_acts, milter_proto;
1121 
1122 	GACOPYZ_ASSERT(srv);
1123 
1124 	if (!(srv->flags & _SRV_CONNECTED)) {
1125 		gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1126 			       _("%s: server not connected, cannot negotiate"),
1127 			       srv->id);
1128 		return MI_FAILURE;
1129 	}
1130 	if (srv->flags & _SRV_READY) {
1131 		gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1132 			       _("%s: server already passed negotiation"),
1133 			       srv->id);
1134 		return MI_FAILURE;
1135 	}
1136 
1137 	ibuf[0] = htonl(srv->version);
1138 	ibuf[1] = htonl(srv->acts);
1139 	ibuf[2] = htonl(srv->proto);
1140 	if (gacopyz_send_command(&srv->iod, SMFIC_OPTNEG, ibuf, sizeof ibuf))
1141 		return MI_FAILURE;
1142 
1143 	if (gacopyz_read_command(&srv->iod, GACOPYZ_TO_READ, &cmd, &size,
1144 				 &srv->buf, &srv->bufsize)) {
1145 		gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
1146 			       _("error reading reply to SMFIC_OPTNEG: %s"),
1147 			       strerror(errno));
1148 		return MI_FAILURE;
1149 	}
1150 	if (cmd != SMFIC_OPTNEG) {
1151 		gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
1152 			       _("gacopyz_srv_negotiate: got %c instead of %c"),
1153 			       cmd, SMFIC_OPTNEG);
1154 		return MI_FAILURE;
1155 	}
1156 
1157 	if (size < sizeof ibuf) {
1158 		gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
1159 			       _("gacopyz_srv_negotiate: not enough data returned"));
1160 		return MI_FAILURE;
1161 	}
1162 
1163 	milter_version = ntohl(((gacopyz_uint32_t*)srv->buf)[0]);
1164 	milter_acts    = ntohl(((gacopyz_uint32_t*)srv->buf)[1]);
1165 	milter_proto   = ntohl(((gacopyz_uint32_t*)srv->buf)[2]);
1166 
1167 	/* Check version */
1168 	if (milter_version == 1 || milter_version > SMFI_VERSION
1169 	    || milter_version > srv->version) {
1170 		gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
1171 			       _("client requested unsupported milter "
1172 				 "version: %#lx"), milter_version);
1173 		return MI_FAILURE;
1174 	}
1175 
1176 	/* Check action capabilities */
1177 	if ((milter_acts & srv->acts) != milter_acts) {
1178 		gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
1179 			       _("filter abilities %#lx do not match MTA "
1180 			         "milter abilities %#lx"),
1181 			       milter_acts, srv->acts);
1182 		return MI_FAILURE;
1183 	}
1184 
1185 	/* Check protocol */
1186 	if ((milter_proto & srv->proto) != milter_proto) {
1187 		gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
1188 			       _("protocol abilities %#lx do not match MTA "
1189 			         "milter abilities %#lx"),
1190 			       milter_proto, srv->proto);
1191 		return MI_FAILURE;
1192 	}
1193 
1194 	if (milter_version > 3
1195 	    && srv->bufsize > 3 * sizeof(gacopyz_uint32_t))
1196 		read_macros(srv);
1197 
1198 	srv->version  = milter_version;
1199 	srv->acts     = milter_acts;
1200 	srv->proto    = milter_proto;
1201 
1202 	srv->flags |= _SRV_READY;
1203 	return MI_SUCCESS;
1204 }
1205 
1206 static struct command_assoc {
1207 	unsigned char cmd;
1208 	int pflag;
1209 	const char *action;
1210 	const char *code;
1211 } command_tab[] = {
1212 	{ SMFIC_CONNECT, SMFIP_NOCONNECT,  "connect",  "554" },
1213 	{ SMFIC_HELO,    SMFIP_NOHELO,     "helo",     "550" },
1214 	{ SMFIC_MAIL,    SMFIP_NOMAIL,     "mail",     "550 5.7.1" },
1215 	{ SMFIC_RCPT,    SMFIP_NORCPT,     "rcpt",     "550 5.7.1" },
1216 	{ SMFIC_HEADER,  SMFIP_NOHDRS,     "header",   "550 5.7.1" },
1217 	{ SMFIC_BODY,    SMFIP_NOBODY,     "body",     "554 5.7.1" },
1218 	{ SMFIC_EOH,     SMFIP_NOEOH,      "eoh",      "550 5.7.1" },
1219 	{ SMFIC_UNKNOWN, SMFIP_NOUNKNOWN,  "unknown",  "550 5.7.1" },
1220 	{ SMFIC_DATA,    SMFIP_NODATA,     "data",     "550 5.7.1" },
1221 	{ 0,             0,                "default",  "550 5.7.1" }
1222 };
1223 
1224 static struct command_assoc *
find_cmd_assoc(unsigned char cmd)1225 find_cmd_assoc(unsigned char cmd)
1226 {
1227 	struct command_assoc *cp;
1228 	for (cp = command_tab; cp->cmd; cp++)
1229 		if (cp->cmd == cmd)
1230 			break;
1231 	return cp;
1232 }
1233 
1234 #define is_rfc822_reject_code(s) \
1235    (((s)[0] == '4' || (s)[0] == '5') \
1236     && isascii((s)[1]) && isdigit((s)[1]) \
1237     && isascii((s)[2]) && isdigit((s)[2]))
1238 
1239 #define DEFREPLY "Command rejected"
1240 
1241 static void
verify_reply(gacopyz_srv_t srv,size_t size,const char * reject_code)1242 verify_reply(gacopyz_srv_t srv, size_t size, const char *reject_code)
1243 {
1244 	if (size < 3 || !is_rfc822_reject_code(srv->buf)) {
1245 		srv_ensure_space(srv,
1246 				 strlen(reject_code) + 1
1247 				 + sizeof(DEFREPLY));
1248 
1249 		strcpy(srv->buf, reject_code);
1250 		strcat(srv->buf, " ");
1251 		strcat(srv->buf, DEFREPLY);
1252 	}
1253 }
1254 
1255 int
gacopyz_srv_send_macros(gacopyz_srv_t srv,unsigned char cmd)1256 gacopyz_srv_send_macros(gacopyz_srv_t srv, unsigned char cmd)
1257 {
1258 	size_t size;
1259 
1260 	switch (cmd) {
1261 	case SMFIC_CONNECT:
1262 	case SMFIC_HELO:
1263 	case SMFIC_MAIL:
1264 	case SMFIC_RCPT:
1265 	case SMFIC_DATA:
1266 	case SMFIC_BODYEOB:
1267 	case SMFIC_EOH:
1268 		srv_format_macros(srv, cmd, &size);
1269 		if (gacopyz_send_command(&srv->iod, SMFIC_MACRO,
1270 					 srv->buf, size))
1271 			return MI_FAILURE;
1272 	}
1273 	return MI_SUCCESS;
1274 }
1275 
1276 int
gacopyz_srv_send_command(gacopyz_srv_t srv,unsigned char cmd,const void * data,size_t datasize)1277 gacopyz_srv_send_command(gacopyz_srv_t srv, unsigned char cmd,
1278 			 const void *data, size_t datasize)
1279 {
1280 	unsigned char rcmd;
1281 	size_t size;
1282 	struct command_assoc *cp;
1283 	int again;
1284 	int t;
1285 
1286 	gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1287 		       _("gacopyz_srv_send_command: size=%lu, cmd=%c"),
1288 		       datasize, cmd);
1289 
1290 	cp = find_cmd_assoc(cmd);
1291 
1292 	srv->resp_ptr = NULL;
1293 	srv->resp_size = 0;
1294 	if (cp->pflag && (srv->proto & cp->pflag))
1295 		return SMFIR_CONTINUE;
1296 
1297 	if (gacopyz_srv_send_macros(srv, cmd))
1298 		return srv->onerror;
1299 
1300 	if (gacopyz_send_command(&srv->iod, cmd, data, datasize))
1301 		return srv->onerror;
1302 
1303 	if (cmd == SMFIC_HEADER && (srv->proto & SMFIP_NOHREPL))
1304 		return SMFIR_CONTINUE;
1305 
1306 	if (cmd == SMFIC_BODYEOB)
1307 		t = GACOPYZ_TO_EOM;
1308 	else
1309 		t = GACOPYZ_TO_READ;
1310 	do {
1311 		again = 0;
1312 		if (gacopyz_read_command(&srv->iod, t, &rcmd, &size,
1313 					 &srv->buf, &srv->bufsize)) {
1314 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
1315 				       _("error reading reply to '%c': %s"),
1316 				       cmd, strerror(errno));
1317 			return srv->onerror;
1318 		}
1319 
1320 		switch (rcmd) {
1321 		case SMFIR_REPLYCODE:
1322 			verify_reply(srv, size, cp->code);
1323 			gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1324 				       "action=%s, reject=%s",
1325 				       cp->action, srv->buf);
1326 			srv->resp_ptr = srv->buf;
1327 			srv->resp_size = size;
1328 			switch (cmd) {
1329 			case SMFIC_BODY:
1330 			case SMFIC_HEADER:
1331 			case SMFIC_EOH:
1332 				srv->flags |= GACOPYZ_SRV_DISABLED
1333 					       | _SRV_CLRDIS;
1334 			}
1335 			break;
1336 
1337 		case SMFIR_REJECT:
1338 			gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1339 				       "action=%s, reject",
1340 				       cp->action);
1341 			break;
1342 
1343 		case SMFIR_DISCARD:
1344 			gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1345 				       "action=%s, discard",
1346 				       cp->action);
1347 			srv->flags |= GACOPYZ_SRV_DISABLED | _SRV_CLRDIS;
1348 			break;
1349 
1350 		case SMFIR_TEMPFAIL:
1351 			gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1352 				       "action=%s, tempfail",
1353 				       cp->action);
1354 			break;
1355 
1356 		case SMFIR_ACCEPT:
1357 			gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1358 				       "action=%s, accept",
1359 				       cp->action);
1360 			srv->flags |= GACOPYZ_SRV_DISABLED | _SRV_CLRDIS;
1361 			break;
1362 
1363 		case SMFIR_CONTINUE:
1364 			gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1365 				       "action=%s, continue",
1366 				       cp->action);
1367 			break;
1368 
1369 		case SMFIR_ADDRCPT:
1370 		case SMFIR_DELRCPT:
1371 		case SMFIR_REPLBODY:
1372 		case SMFIR_CHGHEADER:
1373 		case SMFIR_ADDHEADER:
1374 		case SMFIR_INSHEADER:
1375 			again = 1;
1376 			srv->resp_ptr = srv->buf;
1377 			srv->resp_size = size;
1378 			gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1379 				       "action=%s, response=%c",
1380 				       cp->action, rcmd);
1381 			break;
1382 
1383 		case SMFIR_PROGRESS:
1384 			if (cmd == SMFIC_BODYEOB) {
1385 				gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,
1386 					       "action=%s, progress",
1387 					       cp->action);
1388 				again = 1;
1389 				break;
1390 			}
1391 			/* fall through */
1392 
1393 		default:
1394 			gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
1395 				       "action=%s, unhandled response=%c",
1396 				       cp->action, rcmd);
1397 			/*FIXME: Change to error state? */
1398 			rcmd = srv->onerror;
1399 			break;
1400 		}
1401 
1402 		if (srv->cb_reply)
1403 			srv->cb_reply(srv, cmd, rcmd, srv->cb_data);
1404 	} while (again);
1405 
1406 	return rcmd;
1407 }
1408 
1409 int
gacopyz_srv_reply(gacopyz_srv_t srv,char ** pmsg,size_t * psize)1410 gacopyz_srv_reply(gacopyz_srv_t srv, char **pmsg, size_t *psize)
1411 {
1412 	size_t i, j;
1413 	char *buf;
1414 
1415 	if (!srv->resp_ptr) {
1416 		errno = ENOMSG;
1417 		return MI_FAILURE;
1418 	}
1419 	buf = malloc(srv->resp_size + 1);
1420 	if (!buf)
1421 		return MI_FAILURE;
1422 	for (i = j = 0; i < srv->resp_size; i++, j++) {
1423 		if (srv->resp_ptr[i] == '%' && srv->resp_ptr[i+1] == '%')
1424 			i++;
1425 		buf[j] = srv->resp_ptr[i];
1426 	}
1427 	buf[j] = 0;
1428 
1429 	*pmsg = buf;
1430 	if (psize)
1431 		*psize = j;
1432 	return 0;
1433 }
1434 
1435 void
gacopyz_srv_reply_raw(gacopyz_srv_t srv,char ** msg,size_t * size)1436 gacopyz_srv_reply_raw(gacopyz_srv_t srv, char **msg, size_t *size)
1437 {
1438 	*msg = srv->resp_ptr;
1439 	*size = srv->resp_size;
1440 }
1441 
1442 int
gacopyz_srv_abort(gacopyz_srv_t srv)1443 gacopyz_srv_abort(gacopyz_srv_t srv)
1444 {
1445 	GACOPYZ_ASSERT(srv);
1446 	gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG, "gacopyz_srv_abort");
1447 	if (srv->flags & _SRV_CLRDIS)
1448 		gacopyz_srv_flags(srv, GACOPYZ_SRV_DISABLED, gacopyz_flag_clr);
1449 	return gacopyz_send_command(&srv->iod, SMFIC_ABORT, NULL, 0);
1450 }
1451 
1452 int
gacopyz_srv_quit(gacopyz_srv_t srv)1453 gacopyz_srv_quit(gacopyz_srv_t srv)
1454 {
1455 	GACOPYZ_ASSERT(srv);
1456 	gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG, "gacopyz_srv_quit");
1457 	srv->flags = _SRV_CONNECTED;
1458 	return gacopyz_send_command(&srv->iod, SMFIC_QUIT, NULL, 0);
1459 }
1460 
1461 #define _SRV_ASSERT_FLAG(srv, mask, fun)				\
1462 	do {								\
1463 		if (!((srv)->flags & mask)) {				\
1464 			gacopyz_io_log(&srv->iod, SMI_LOG_DEBUG,	\
1465 				       _("%s: %s called with flags=%x"), \
1466 				       (srv)->id, mask);		\
1467 			return srv->onerror;				\
1468 		}							\
1469 	} while (0)
1470 
1471 static void
format_argv(gacopyz_srv_t srv,char ** argv,char ** buf,size_t * psize)1472 format_argv(gacopyz_srv_t srv, char **argv, char **buf, size_t *psize)
1473 {
1474 	size_t len;
1475 	char **ap;
1476 	char *p;
1477 
1478 	for (len = 1, ap = argv; *ap; ap++)
1479 		len += strlen(*ap) + 1;
1480 
1481 	GACOPYZ_ALLOC(srv, *buf = malloc(len));
1482 
1483 	for (p = *buf, ap = argv; *ap; ap++) {
1484 		size_t len = strlen(*ap) + 1;
1485 		memcpy(p, *ap, len);
1486 		p += len;
1487 	}
1488 	*p = 0;
1489 	*psize = len;
1490 }
1491 
1492 int
gacopyz_srv_connect(gacopyz_srv_t srv,const char * hostname,struct sockaddr * sa)1493 gacopyz_srv_connect(gacopyz_srv_t srv, const char *hostname,
1494 		    struct sockaddr *sa)
1495 {
1496 	int rc;
1497 	int family = SMFIA_UNKNOWN;
1498 	char *arg;
1499 	size_t argsize, len;
1500 	size_t size;
1501 	char *s = "";
1502 	char *sbuf = NULL;
1503 	char *buf;
1504 	unsigned short port = 0;
1505 
1506 	GACOPYZ_ASSERT(srv);
1507 	_SRV_ASSERT_FLAG(srv, _SRV_READY, "gacopyz_srv_connect");
1508 	if (srv->flags & GACOPYZ_SRV_DISABLED)
1509 		return SMFIR_CONTINUE;
1510 
1511 	if (sa) {
1512 		switch (sa->sa_family) {
1513 		case AF_UNIX: {
1514 			struct sockaddr_un *sptr = (struct sockaddr_un *) sa;
1515 			family = SMFIA_UNIX;
1516 			port = 0;
1517 			len = strlen(sptr->sun_path);
1518 			s = sptr->sun_path;
1519 			break;
1520 		}
1521 
1522 		case AF_INET: {
1523 			struct sockaddr_in *sptr = (struct sockaddr_in *) sa;
1524 
1525 			family = SMFIA_INET;
1526 			port = sptr->sin_port;
1527 			s = inet_ntoa(sptr->sin_addr);
1528 			if (!s) {
1529 				gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
1530 					       "inet_ntoa: %s",
1531 					       strerror(errno));
1532 				return MI_FAILURE;
1533 			}
1534 			break;
1535 		}
1536 
1537 #ifdef GACOPYZ_IPV6
1538 		case AF_INET6: {
1539 			char host[NI_MAXHOST];
1540 			char service[NI_MAXSERV];
1541 			int rc;
1542 
1543 			rc = getnameinfo(sa, sizeof(struct sockaddr_in6),
1544 					 host, sizeof(host),
1545 					 service, sizeof(service),
1546 					 NI_NUMERICHOST|NI_NUMERICSERV);
1547 			if (rc) {
1548 				gacopyz_io_log(&srv->iod, SMI_LOG_ERR,
1549 					       "getnameinfo: %s",
1550 					       gai_strerror(rc));
1551 				return MI_FAILURE;
1552 			}
1553 
1554 			family = SMFIA_INET6;
1555 			port = htons(atoi(service));
1556 			GACOPYZ_ALLOC(srv,
1557 				      sbuf = malloc(GACOPYZ_IPV6PREFIX_LEN +
1558 						    strlen(host) + 1));
1559 			s = sbuf;
1560 			strcat(strcpy(s, GACOPYZ_IPV6PREFIX_STR), host);
1561 			break;
1562 		}
1563 
1564 #endif
1565 		default:
1566 			break;
1567 		}
1568 	}
1569 
1570 	len = strlen(s);
1571 	argsize = 2 + sizeof(port) + len;
1572 
1573 	GACOPYZ_ALLOC(srv, arg = malloc(argsize));
1574 	arg[0] = family;
1575 	memcpy(arg + 1, &port, sizeof(port));
1576 	memcpy(arg + 1 + sizeof(port), s, len + 1);
1577 	free(sbuf);
1578 
1579 	len = strlen(hostname);
1580 	size = 1 + len + 1 + argsize;
1581 	GACOPYZ_ALLOC(srv, buf = malloc(size));
1582 
1583 	memcpy(buf, hostname, len + 1);
1584 	memcpy(buf + len + 1, arg, argsize);
1585 	buf[len + argsize + 1] = 0;
1586 	free(arg);
1587 
1588 	rc = gacopyz_srv_send_command(srv, SMFIC_CONNECT, buf, size);
1589 	free(buf);
1590 
1591 	return rc;
1592 }
1593 
1594 int
gacopyz_srv_helo(gacopyz_srv_t srv,const char * domain)1595 gacopyz_srv_helo(gacopyz_srv_t srv, const char *domain)
1596 {
1597 	GACOPYZ_ASSERT(srv);
1598 	_SRV_ASSERT_FLAG(srv, _SRV_READY, "gacopyz_srv_helo");
1599 	if (srv->flags & GACOPYZ_SRV_DISABLED)
1600 		return SMFIR_CONTINUE;
1601 	return gacopyz_srv_send_command(srv, SMFIC_HELO,
1602 					domain, strlen(domain) + 1);
1603 }
1604 
1605 int
gacopyz_srv_envfrom(gacopyz_srv_t srv,char ** argv)1606 gacopyz_srv_envfrom(gacopyz_srv_t srv, char **argv)
1607 {
1608 	int rc;
1609 	size_t size;
1610 	char *buf;
1611 
1612 	GACOPYZ_ASSERT(srv);
1613 	_SRV_ASSERT_FLAG(srv, _SRV_READY, "gacopyz_srv_envfrom");
1614 	if (srv->flags & GACOPYZ_SRV_DISABLED)
1615 		return SMFIR_CONTINUE;
1616 
1617 	format_argv(srv, argv, &buf, &size);
1618 	rc = gacopyz_srv_send_command(srv, SMFIC_MAIL, buf, size);
1619 	free(buf);
1620 
1621 	return rc;
1622 }
1623 
1624 int
gacopyz_srv_envrcpt(gacopyz_srv_t srv,char ** argv)1625 gacopyz_srv_envrcpt(gacopyz_srv_t srv, char **argv)
1626 {
1627 	int rc;
1628 	size_t size;
1629 	char *buf;
1630 
1631 	GACOPYZ_ASSERT(srv);
1632 	_SRV_ASSERT_FLAG(srv, _SRV_READY, "gacopyz_srv_envrcpt");
1633 	if (srv->flags & GACOPYZ_SRV_DISABLED)
1634 		return SMFIR_CONTINUE;
1635 
1636 	format_argv(srv, argv, &buf, &size);
1637 	rc = gacopyz_srv_send_command(srv, SMFIC_RCPT, buf, size);
1638 	free(buf);
1639 
1640 	return rc;
1641 }
1642 
1643 int
gacopyz_srv_data(gacopyz_srv_t srv)1644 gacopyz_srv_data(gacopyz_srv_t srv)
1645 {
1646 	GACOPYZ_ASSERT(srv);
1647 	_SRV_ASSERT_FLAG(srv, _SRV_READY, "gacopyz_srv_data");
1648 	if (srv->flags & GACOPYZ_SRV_DISABLED)
1649 		return SMFIR_CONTINUE;
1650 	return gacopyz_srv_send_command(srv, SMFIC_DATA, NULL, 0);
1651 }
1652 
1653 int
gacopyz_srv_header(gacopyz_srv_t srv,char * name,char * value)1654 gacopyz_srv_header(gacopyz_srv_t srv, char *name, char *value)
1655 {
1656 	int rc;
1657 	size_t nlen, vlen, size;
1658 	char *buf;
1659 
1660 	GACOPYZ_ASSERT(srv && name && value);
1661 	_SRV_ASSERT_FLAG(srv, _SRV_READY, "gacopyz_srv_header");
1662 	if (srv->flags & GACOPYZ_SRV_DISABLED)
1663 		return SMFIR_CONTINUE;
1664 
1665 	nlen = strlen(name);
1666 	vlen = strlen(value);
1667 	size = nlen + vlen + 2;
1668 	GACOPYZ_ALLOC(srv, buf = malloc(size));
1669 	memcpy(buf, name, nlen + 1);
1670 	memcpy(buf + nlen + 1, value, vlen + 1);
1671 	rc = gacopyz_srv_send_command(srv, SMFIC_HEADER, buf, size);
1672 	free(buf);
1673 
1674 	return rc;
1675 }
1676 
1677 int
gacopyz_srv_eoh(gacopyz_srv_t srv)1678 gacopyz_srv_eoh(gacopyz_srv_t srv)
1679 {
1680 	GACOPYZ_ASSERT(srv);
1681 	_SRV_ASSERT_FLAG(srv, _SRV_READY, "gacopyz_srv_eoh");
1682 	if (srv->flags & GACOPYZ_SRV_DISABLED)
1683 		return SMFIR_CONTINUE;
1684 	return gacopyz_srv_send_command(srv, SMFIC_EOH, NULL, 0);
1685 }
1686 
1687 int
gacopyz_srv_body(gacopyz_srv_t srv,unsigned char * str,size_t size)1688 gacopyz_srv_body(gacopyz_srv_t srv, unsigned char *str, size_t size)
1689 {
1690 	GACOPYZ_ASSERT(srv);
1691 	_SRV_ASSERT_FLAG(srv, _SRV_READY, "gacopyz_srv_body");
1692 	if (srv->flags & GACOPYZ_SRV_DISABLED)
1693 		return SMFIR_CONTINUE;
1694 	return gacopyz_srv_send_command(srv, SMFIC_BODY, str, size);
1695 }
1696 
1697 int
gacopyz_srv_eom(gacopyz_srv_t srv,unsigned char * chunk,size_t size)1698 gacopyz_srv_eom(gacopyz_srv_t srv, unsigned char *chunk, size_t size)
1699 {
1700 	int disabled;
1701 
1702 	GACOPYZ_ASSERT(srv);
1703 	_SRV_ASSERT_FLAG(srv, _SRV_READY, "gacopyz_srv_eom");
1704 	disabled = srv->flags & GACOPYZ_SRV_DISABLED;
1705 	if (srv->flags & _SRV_CLRDIS)
1706 		gacopyz_srv_flags(srv, GACOPYZ_SRV_DISABLED, gacopyz_flag_clr);
1707 	if (disabled)
1708 		return SMFIR_CONTINUE;
1709 	return gacopyz_srv_send_command(srv, SMFIC_BODYEOB, chunk, size);
1710 }
1711 
1712 int
gacopyz_srv_close(gacopyz_srv_t srv)1713 gacopyz_srv_close(gacopyz_srv_t srv)
1714 {
1715 	GACOPYZ_ASSERT(srv);
1716 	_SRV_ASSERT_FLAG(srv, _SRV_CONNECTED, "gacopyz_srv_close");
1717 	close(srv->iod.sd);
1718 	srv->flags = 0;
1719 	return MI_SUCCESS;
1720 }
1721 
1722 
1723