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