1 /* $OpenBSD: iobuf.h,v 1.1 2012/01/29 00:32:51 eric Exp $ */
2 /*
3 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/queue.h>
21
22 #include <ctype.h>
23 #include <err.h>
24 #include <errno.h>
25 #include <getopt.h>
26 #include <netdb.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <vis.h>
33
34 #include "iobuf.h"
35
36 #include "smtpscript.h"
37
38 void *ssl_connect(int);
39 void ssl_close(void *);
40
41 /* XXX */
42 #define SMTP_LINE_MAX 4096
43
44 enum {
45 OP_BLOCK,
46 OP_REPEAT,
47 OP_RANDOM,
48
49 OP_NOOP,
50
51 OP_FAIL,
52 OP_CALL,
53 OP_CONNECT,
54 OP_DISCONNECT,
55 OP_STARTTLS,
56 OP_SLEEP,
57 OP_WRITE,
58
59 OP_EXPECT_DISCONNECT,
60 OP_EXPECT_SMTP_RESPONSE,
61 };
62
63 struct op {
64 struct op *next;
65 int type;
66 union {
67 struct {
68 int count;
69 struct op *start;
70 struct op *last;
71 } block;
72 struct {
73 struct op *op;
74 int count;
75 } repeat;
76 struct {
77 struct op *block;
78 } random;
79 struct {
80 char *reason;
81 } fail;
82 struct {
83 struct procedure *proc;
84 } call;
85 struct {
86 char *hostname;
87 int portno;
88 } connect;
89 struct {
90 unsigned int ms;
91 } sleep;
92 struct {
93 const void *buf;
94 size_t len;
95 } write;
96 struct {
97 int flags;
98 } expect_smtp;
99 } u;
100 };
101
102 #define RES_OK 0
103 #define RES_SKIP 1
104 #define RES_FAIL 2
105 #define RES_ERROR 3
106
107 struct ctx {
108 int sock;
109 void *ssl;
110 struct iobuf iobuf;
111 int lvl;
112
113 int result;
114 char *reason;
115 };
116
117 static struct op * _op_connect;
118
119 int verbose;
120 int randomdelay; /* between each testcase */
121 int tapout;
122 size_t rundelay; /* between each testcase */
123
124 static size_t test_pass;
125 static size_t test_skip;
126 static size_t test_fail;
127 static size_t test_error;
128 static size_t test_total = 0;
129
130 static struct op *op_add_child(struct op *, const struct op *);
131 static void run_testcase(struct procedure *);
132 static void print_testcase(char *status, char *name, char *reason, char *directive, size_t number);
133 static void process_op(struct ctx *, struct op *);
134 static const char * parse_smtp_response(char *, size_t, char **, int *);
135
136 struct procedure *
procedure_create(struct script * scr,char * name)137 procedure_create(struct script *scr, char *name)
138 {
139 struct procedure *p;
140
141 if (procedure_get_by_name(scr, name)) {
142 warnx("procedure \"%s\" already exists", name);
143 return (NULL);
144 }
145
146 p = calloc(1, sizeof *p);
147 TAILQ_INIT(&p->vars);
148 p->name = strdup(name);
149
150 TAILQ_INSERT_TAIL(&scr->procs, p, entry);
151
152 return (p);
153 }
154
155 struct procedure *
procedure_get_by_name(struct script * scr,const char * name)156 procedure_get_by_name(struct script *scr, const char *name)
157 {
158 struct procedure *p;
159
160 TAILQ_FOREACH(p, &scr->procs, entry)
161 if (!strcmp(name, p->name))
162 return (p);
163
164 return (NULL);
165 }
166
167 int
proc_getvaridx(struct procedure * proc,char * name)168 proc_getvaridx(struct procedure *proc, char *name)
169 {
170 struct variable *v;
171 int n;
172
173 n = 0;
174 TAILQ_FOREACH(v, &proc->vars, entry) {
175 if (!strcmp(name, v->name))
176 return (n);
177 n++;
178 }
179
180 return (-1);
181 }
182
183 int
proc_addvar(struct procedure * proc,char * name)184 proc_addvar(struct procedure *proc, char *name)
185 {
186 struct variable *v;
187
188 printf("adding variable \"%s\"\n", name);
189
190 if (proc_getvaridx(proc, name) != -1)
191 return (-1);
192 v = calloc(1, sizeof *v);
193 v->name = name;
194 TAILQ_INSERT_TAIL(&proc->vars, v, entry);
195
196 return (proc->varcount++);
197 }
198
199 struct op *
op_block(struct op * parent)200 op_block(struct op *parent)
201 {
202 struct op o;
203
204 bzero(&o, sizeof o);
205 o.type = OP_BLOCK;
206
207 return (op_add_child(parent, &o));
208 }
209
210 struct op *
op_repeat(struct op * parent,int count,struct op * op)211 op_repeat(struct op *parent, int count, struct op *op)
212 {
213 struct op o;
214
215 bzero(&o, sizeof o);
216 o.type = OP_REPEAT;
217 o.u.repeat.count = count;
218 o.u.repeat.op = op;
219
220 return (op_add_child(parent, &o));
221 }
222
223 struct op *
op_random(struct op * parent,struct op * op)224 op_random(struct op *parent, struct op *op)
225 {
226 struct op o;
227
228 bzero(&o, sizeof o);
229 o.type = OP_RANDOM;
230 o.u.random.block = op;
231
232 return (op_add_child(parent, &o));
233 }
234
235 struct op *
op_noop(struct op * parent)236 op_noop(struct op *parent)
237 {
238 struct op o;
239
240 bzero(&o, sizeof o);
241 o.type = OP_NOOP;
242
243 return (op_add_child(parent, &o));
244 }
245
246 struct op *
op_call(struct op * parent,struct procedure * proc)247 op_call(struct op *parent, struct procedure *proc)
248 {
249 struct op o;
250
251 bzero(&o, sizeof o);
252 o.type = OP_CALL;
253 o.u.call.proc = proc;
254
255 return (op_add_child(parent, &o));
256 }
257
258 struct op *
op_fail(struct op * parent,char * reason)259 op_fail(struct op *parent, char *reason)
260 {
261 struct op o;
262
263 bzero(&o, sizeof o);
264 o.type = OP_FAIL;
265 o.u.fail.reason = reason;
266
267 return (op_add_child(parent, &o));
268 }
269
270 struct op *
op_connect(struct op * parent,const char * hostname,int portno)271 op_connect(struct op *parent, const char *hostname, int portno)
272 {
273 struct op o;
274
275 bzero(&o, sizeof o);
276 o.type = OP_CONNECT;
277 o.u.connect.hostname = strdup(hostname);
278 o.u.connect.portno = portno;
279 return (op_add_child(parent, &o));
280 }
281
282 struct op *
op_disconnect(struct op * parent)283 op_disconnect(struct op *parent)
284 {
285 struct op o;
286
287 bzero(&o, sizeof o);
288 o.type = OP_DISCONNECT;
289 return (op_add_child(parent, &o));
290 }
291
292 struct op *
op_starttls(struct op * parent)293 op_starttls(struct op *parent)
294 {
295 struct op o;
296
297 bzero(&o, sizeof o);
298 o.type = OP_STARTTLS;
299 return (op_add_child(parent, &o));
300 }
301
302 struct op *
op_sleep(struct op * parent,unsigned int ms)303 op_sleep(struct op *parent, unsigned int ms)
304 {
305 struct op o;
306
307 bzero(&o, sizeof o);
308 o.type = OP_SLEEP;
309 o.u.sleep.ms = ms;
310 return (op_add_child(parent, &o));
311 }
312
313 struct op *
op_write(struct op * parent,const void * buf,size_t len)314 op_write(struct op *parent, const void *buf, size_t len)
315 {
316 struct op o;
317
318 bzero(&o, sizeof o);
319 o.type = OP_WRITE;
320 o.u.write.buf = buf;
321 o.u.write.len = len;
322 return (op_add_child(parent, &o));
323 }
324
325 struct op *
op_printf(struct op * parent,const char * fmt,...)326 op_printf(struct op *parent, const char *fmt, ...)
327 {
328 va_list ap;
329 char *buf;
330 int len;
331
332 va_start(ap, fmt);
333 if ((len = vasprintf(&buf, fmt, ap)) == -1)
334 err(1, "vasprintf");
335 va_end(ap);
336
337 return op_write(parent, buf, len);
338 }
339
340 struct op *
op_expect_disconnect(struct op * parent)341 op_expect_disconnect(struct op *parent)
342 {
343 struct op o;
344
345 bzero(&o, sizeof o);
346 o.type = OP_EXPECT_DISCONNECT;
347 return (op_add_child(parent, &o));
348 }
349
350 struct op *
op_expect_smtp_response(struct op * parent,int flags)351 op_expect_smtp_response(struct op *parent, int flags)
352 {
353 struct op o;
354
355 bzero(&o, sizeof o);
356 o.type = OP_EXPECT_SMTP_RESPONSE;
357 o.u.expect_smtp.flags = flags;
358 return (op_add_child(parent, &o));
359 }
360
361 static void
usage(void)362 usage(void)
363 {
364 extern const char *__progname;
365 errx(1, "Usage: %s [-rvt] [-d delay] script", __progname);
366 }
367
368 int
main(int argc,char ** argv)369 main(int argc, char **argv)
370 {
371 struct script *s;
372 struct procedure *p;
373 int ch;
374
375 while ((ch = getopt(argc, argv, "d:rvt")) != -1) {
376 switch(ch) {
377 case 'v':
378 verbose += 1;
379 break;
380 case 'd':
381 rundelay = atoi(optarg) * 1000;
382 break;
383 case 'r':
384 randomdelay = 1;
385 break;
386 case 't':
387 tapout = 1;
388 break;
389 default:
390 usage();
391 /* NOTREACHED */
392 }
393 }
394 argc -= optind;
395 argv += optind;
396
397 if (argc != 1)
398 usage();
399
400 s = parse_script(argv[0]);
401 if (s == NULL)
402 errx(1, "error reading script file");
403
404 _op_connect = op_connect(NULL, "127.0.0.1", 25);
405
406 if (tapout) {
407 printf("# smtpscript is an SMTP testing framework\n\n");
408 printf("TAP version 13\n");
409 }
410
411 TAILQ_FOREACH(p, &s->procs, entry)
412 if (p->flags & PROC_TESTCASE)
413 run_testcase(p);
414
415 if (tapout)
416 printf("1..%zu\n", test_total);
417 else {
418 printf("passed: %zu/%zu (skipped: %zu, failed: %zu, error: %zu)\n",
419 test_pass,
420 test_total,
421 test_skip,
422 test_fail,
423 test_error);
424 }
425 return (0);
426 }
427
428 static struct op *
op_add_child(struct op * parent,const struct op * op)429 op_add_child(struct op *parent, const struct op *op)
430 {
431 struct op *n;
432
433 n = malloc(sizeof *n);
434 if (n == NULL)
435 err(1, "malloc");
436
437 memmove(n, op, sizeof *n);
438 n->next = NULL;
439
440 /* printf("op:%p type:%i parent: %p\n", n, n->type, parent); */
441
442 if (parent) {
443 if (parent->u.block.start == NULL)
444 parent->u.block.start = n;
445 if (parent->u.block.last)
446 parent->u.block.last->next = n;
447 parent->u.block.last = n;
448 parent->u.block.count += 1;
449 }
450
451 return (n);
452 }
453
454 static void
run_testcase(struct procedure * proc)455 run_testcase(struct procedure *proc)
456 {
457 struct ctx c;
458 uint32_t rdelay;
459
460 bzero(&c, sizeof c);
461 c.sock = -1;
462 c.lvl = 1;
463
464 if (rundelay) {
465 if (randomdelay)
466 rdelay = arc4random_uniform(rundelay);
467 else
468 rdelay = rundelay;
469 usleep(rdelay);
470 }
471
472 fflush(stdout);
473
474 if (verbose > 1)
475 printf("\n");
476
477 if (!(proc->flags & PROC_NOCONNECT))
478 process_op(&c, _op_connect);
479 process_op(&c, proc->root);
480
481 if (c.sock != -1)
482 close(c.sock);
483 if (c.ssl)
484 ssl_close(c.ssl);
485 iobuf_clear(&c.iobuf);
486
487 if (verbose > 1) {
488 printf("# Done with test-case \"%s\": ", proc->name);
489 }
490
491 switch (c.result) {
492 case RES_OK:
493 test_total += 1;
494 if (proc->flags & PROC_EXPECTFAIL) {
495 print_testcase("not ok", proc->name, c.reason, "TODO", test_total); // XPass
496 test_fail += 1;
497 } else if (proc->flags & PROC_SKIP) {
498 test_skip += 1;
499 print_testcase("ok", proc->name, c.reason, "SKIP", test_total);
500 }
501 else {
502 print_testcase("ok", proc->name, c.reason, NULL, test_total);
503 test_pass += 1;
504 }
505
506 break;
507
508 case RES_SKIP:
509 test_skip += 1;
510 test_total += 1;
511 print_testcase("not ok", proc->name, c.reason, "SKIP", test_total);
512 break;
513
514 case RES_FAIL:
515 test_total += 1;
516 if (proc->flags & PROC_EXPECTFAIL) {
517 print_testcase("not ok", proc->name, c.reason, "TODO", test_total); // XFail
518 test_pass += 1;
519 } else if (proc->flags & PROC_SKIP) {
520 test_skip += 1;
521 print_testcase("ok", proc->name, c.reason, "SKIP", test_total);
522 }
523 else {
524 print_testcase("not ok", proc->name, c.reason, NULL, test_total);
525 test_fail += 1;
526 }
527
528 break;
529
530 case RES_ERROR:
531 test_error += 1;
532 test_total += 1;
533 print_testcase("not ok", proc->name, c.reason, NULL, test_total);
534 break;
535 }
536
537 if (verbose > 1) {
538 printf("\n");
539 }
540
541 }
542
print_testcase(char * status,char * name,char * reason,char * directive,size_t number)543 void print_testcase(char *status, char *name, char *reason, char *directive, size_t number)
544 {
545 printf("%s %zu", status, number);
546 if (directive)
547 printf(" - %s # %s\n", name, directive);
548 else
549 if (reason)
550 printf(" - %s # %s\n", name, reason);
551 else
552 printf(" - %s\n", name);
553 }
554
555 static size_t
strvisx2(char * dst,const char * src,size_t srclen,int flag)556 strvisx2(char *dst, const char *src, size_t srclen, int flag)
557 {
558 size_t n, r, i;
559
560 n = strvisx(dst, src, srclen, flag);
561 if (n == 0)
562 return (0);
563
564 r = n;
565 for (i = n - 1; i; i--) {
566 if (dst[i] == '\r') {
567 memmove(dst + i + 2, dst + i + 1, n + 1 - i);
568 dst[i+1] = 'r';
569 dst[i] = '\\';
570 r++;
571 } else if (dst[i] == '"') {
572 memmove(dst + i + 2, dst + i + 1, n + 1 - i);
573 dst[i+1] = '"';
574 dst[i] = '\\';
575 r++;
576 }
577 }
578
579 return (r);
580 }
581
582 static const char *
show_data(const char * src,size_t len,size_t max)583 show_data(const char *src, size_t len, size_t max)
584 {
585 static char buf[8192 + 3];
586 char tmp[256];
587 size_t l, n;
588
589 l = len;
590 if (len > 2048)
591 l = 2048;
592
593 buf[0] = '"';
594 n = strvisx2(&buf[1], src, l, VIS_SAFE | VIS_NL | VIS_TAB | VIS_CSTYLE);
595 if (n >= max) {
596 snprintf(tmp, sizeof tmp, "...\" [%zu]", l);
597 buf[max - strlen(tmp)] = '\0';
598 strlcat(buf, tmp, sizeof(buf));
599 } else {
600 strlcat(buf, "\"", sizeof(buf));
601 }
602
603 return (buf);
604 }
605
606 static void
print_op(struct op * op,int lvl)607 print_op(struct op *op, int lvl)
608 {
609
610
611 if (op->type == OP_BLOCK)
612 return;
613
614 while (lvl--)
615 printf(" ");
616
617 switch(op->type) {
618
619 case OP_REPEAT:
620 printf("=> repeat: %i\n", op->u.repeat.count);
621 break;
622
623 case OP_RANDOM:
624 printf("=> random: %i\n", op->u.random.block->u.block.count);
625 break;
626
627 case OP_NOOP:
628 printf("=> noop\n");
629 break;
630
631 case OP_FAIL:
632 printf("=> fail: %s\n", op->u.fail.reason);
633 break;
634
635 case OP_CALL:
636 printf("=> call: %s\n", op->u.call.proc->name);
637 break;
638
639 case OP_CONNECT:
640 printf("=> connect %s:%i\n",
641 op->u.connect.hostname,
642 op->u.connect.portno);
643 break;
644
645 case OP_DISCONNECT:
646 printf("=> disconnect\n");
647 break;
648
649 case OP_STARTTLS:
650 printf("=> starttls\n");
651 break;
652
653 case OP_SLEEP:
654 printf("=> sleep %ims\n", op->u.sleep.ms);
655 break;
656
657 case OP_WRITE:
658 printf("=> write %s\n",
659 show_data(op->u.write.buf, op->u.write.len, 70));
660 break;
661
662 case OP_EXPECT_DISCONNECT:
663 printf("<= disconnect\n");
664 break;
665
666 case OP_EXPECT_SMTP_RESPONSE:
667 printf("<= smtp-response 0x%04x\n", op->u.expect_smtp.flags);
668 break;
669
670 default:
671 printf("<> ??? %i;\n", op->type);
672 break;
673 }
674 }
675
676
677 static void
set_failure(struct ctx * ctx,int res,const char * fmt,...)678 set_failure(struct ctx *ctx, int res, const char *fmt, ...)
679 {
680 va_list ap;
681 int len;
682
683 ctx->result = res;
684 va_start(ap, fmt);
685 if ((len = vasprintf(&ctx->reason, fmt, ap)) == -1)
686 err(1, "vasprintf");
687 va_end(ap);
688 }
689
690 static void
process_op(struct ctx * ctx,struct op * op)691 process_op(struct ctx *ctx, struct op *op)
692 {
693 struct addrinfo hints, *a, *ai;
694 struct op *o;
695 struct iobuf *iobuf;
696 int i, r, s, save_errno, cont;
697 const char *cause;
698 char buf[16], *servname, *line;
699 ssize_t n;
700 size_t len;
701 const char *e;
702
703 if (verbose > 1)
704 print_op(op, ctx->lvl);
705
706 iobuf = &ctx->iobuf;
707
708 switch(op->type) {
709
710 case OP_BLOCK:
711 ctx->lvl += 1;
712 for (o = op->u.block.start; o; o = o->next) {
713 process_op(ctx, o);
714 if (ctx->result)
715 break;
716 }
717 ctx->lvl -= 1;
718 break;
719
720 case OP_REPEAT:
721 ctx->lvl += 1;
722 for (i = 0; i < op->u.repeat.count; i++) {
723 process_op(ctx, op->u.repeat.op);
724 if (ctx->result)
725 break;
726 }
727 ctx->lvl -= 1;
728 break;
729
730 case OP_RANDOM:
731
732 if (op->u.random.block->u.block.count == 0)
733 return;
734
735 ctx->lvl += 1;
736
737 i = arc4random_uniform(op->u.random.block->u.block.count);
738 for (o = op->u.random.block->u.block.start; i; i--, o = o->next)
739 ;
740 process_op(ctx, o);
741 if (ctx->result)
742 break;
743 ctx->lvl -= 1;
744 break;
745
746 case OP_NOOP:
747 break;
748
749 case OP_FAIL:
750 set_failure(ctx, RES_FAIL, op->u.fail.reason);
751 break;
752
753 case OP_CALL:
754 process_op(ctx, op->u.call.proc->root);
755 break;
756
757 case OP_CONNECT:
758 if (ctx->sock != -1)
759 close(ctx->sock);
760 ctx->sock = -1;
761 iobuf_clear(iobuf);
762
763 servname = NULL;
764 if (op->u.connect.portno) {
765 snprintf(buf, sizeof buf, "%i", op->u.connect.portno);
766 servname = buf;
767 }
768 bzero(&hints, sizeof hints);
769 hints.ai_socktype = SOCK_STREAM;
770 hints.ai_protocol = IPPROTO_TCP;
771 r = getaddrinfo(op->u.connect.hostname, servname, &hints, &ai);
772 if (r) {
773 set_failure(ctx, RES_ERROR,
774 "failed to connect to %s:%s: %s",
775 op->u.connect.hostname, servname, gai_strerror(r));
776 return;
777 }
778
779 s = -1;
780 for(a = ai; a; a = a->ai_next) {
781 s = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
782 if (s == -1) {
783 cause = "socket";
784 continue;
785 }
786 if (connect(s, a->ai_addr, a->ai_addrlen) == -1) {
787 cause = "connect";
788 save_errno = errno;
789 close(s);
790 errno = save_errno;
791 s = -1;
792 continue;
793 }
794 break; /* okay we got one */
795 }
796 freeaddrinfo(ai);
797 if (s == -1) {
798 set_failure(ctx, RES_ERROR,
799 "failed to connect to %s:%s: %s",
800 op->u.connect.hostname, servname, cause);
801 } else {
802 ctx->sock = s;
803 iobuf_init(iobuf, 0, 0);
804 }
805 break;
806
807 case OP_DISCONNECT:
808 if (ctx->sock != -1)
809 close(ctx->sock);
810 ctx->sock = -1;
811 iobuf_clear(iobuf);
812 break;
813
814 case OP_STARTTLS:
815 if (ctx->ssl)
816 set_failure(ctx, RES_ERROR, "SSL context already here");
817 else if ((ctx->ssl = ssl_connect(ctx->sock)) == NULL)
818 set_failure(ctx, RES_ERROR, "SSL connection failed");
819 break;
820
821 case OP_SLEEP:
822 usleep(op->u.sleep.ms * 1000);
823 break;
824
825 case OP_WRITE:
826 iobuf_queue(iobuf, op->u.write.buf, op->u.write.len);
827 if (ctx->ssl)
828 r = iobuf_flush_ssl(iobuf, ctx->ssl);
829 else
830 r = iobuf_flush(iobuf, ctx->sock);
831 switch (r) {
832 case 0:
833 break;
834 case IOBUF_CLOSED:
835 set_failure(ctx, RES_FAIL, "connection closed");
836 break;
837 case IOBUF_WANT_WRITE:
838 set_failure(ctx, RES_ERROR, "iobuf_write(): WANT_WRITE");
839 break;
840 case IOBUF_ERROR:
841 set_failure(ctx, RES_ERROR, "IO error");
842 break;
843 case IOBUF_SSLERROR:
844 set_failure(ctx, RES_ERROR, "SSL error");
845 break;
846 default:
847 set_failure(ctx, RES_ERROR, "iobuf_write(): bad value");
848 break;
849 }
850 break;
851
852 case OP_EXPECT_DISCONNECT:
853 if (iobuf_len(iobuf)) {
854 set_failure(ctx, RES_ERROR, "%zu bytes of input left",
855 iobuf_len(iobuf));
856 break;
857 }
858 if (ctx->ssl)
859 n = iobuf_read_ssl(iobuf, ctx->ssl);
860 else
861 n = iobuf_read(iobuf, ctx->sock);
862 switch (n) {
863 case IOBUF_CLOSED:
864 close(ctx->sock);
865 ctx->sock = -1;
866 if (ctx->ssl)
867 ssl_close(ctx->ssl);
868 break;
869 case IOBUF_WANT_READ:
870 set_failure(ctx, RES_ERROR, "iobuf_read(): WANT_READ");
871 break;
872 case IOBUF_ERROR:
873 set_failure(ctx, RES_ERROR, "IO error");
874 break;
875 case IOBUF_SSLERROR:
876 set_failure(ctx, RES_ERROR, "SSL error");
877 break;
878 default:
879 set_failure(ctx, RES_FAIL, "data read: %s",
880 show_data(iobuf_data(iobuf), iobuf_len(iobuf), 70));
881 break;
882 }
883 break;
884
885 case OP_EXPECT_SMTP_RESPONSE:
886 line = NULL;
887 while (1) {
888 line = iobuf_getline(iobuf, &len);
889 if (line) {
890 e = parse_smtp_response(line, len, NULL, &cont);
891 if (e) {
892 set_failure(ctx, RES_FAIL, e);
893 return;
894 }
895 if (!cont) {
896 iobuf_normalize(iobuf);
897 break;
898 }
899 if (!(op->u.expect_smtp.flags
900 & RESP_SMTP_MULTILINE)) {
901 set_failure(ctx, RES_FAIL,
902 "single line response expected");
903 return;
904 }
905 continue;
906 }
907
908 if (iobuf_len(iobuf) >= SMTP_LINE_MAX) {
909 set_failure(ctx, RES_FAIL, "line too long");
910 return;
911 }
912
913 iobuf_normalize(iobuf);
914
915 again:
916 if (ctx->ssl)
917 n = iobuf_read_ssl(iobuf, ctx->ssl);
918 else
919 n = iobuf_read(iobuf, ctx->sock);
920 switch (n) {
921 case IOBUF_CLOSED:
922 set_failure(ctx, RES_FAIL, "connection closed");
923 return;
924 case IOBUF_WANT_READ:
925 goto again;
926 case IOBUF_ERROR:
927 set_failure(ctx, RES_ERROR, "io error");
928 return;
929 case IOBUF_SSLERROR:
930 set_failure(ctx, RES_ERROR, "SSL error");
931 return;
932 default:
933 break;
934 }
935 }
936
937 /* got our response */
938
939 if (verbose > 1) {
940 len = ctx->lvl;
941 while (len--)
942 printf(" ");
943 printf(" >>> %s\n", show_data(line, strlen(line), 70));
944 }
945
946 switch (line[0]) {
947 case '2':
948 case '3':
949 if (!(op->u.expect_smtp.flags & RESP_SMTP_OK))
950 set_failure(ctx, RES_FAIL,
951 "unexpected response code0: %s", line);
952 break;
953 case '4':
954 if (!(op->u.expect_smtp.flags & RESP_SMTP_TEMPFAIL))
955 set_failure(ctx, RES_FAIL,
956 "unexpected response code1: %s", line);
957 break;
958 case '5':
959 if (!(op->u.expect_smtp.flags & RESP_SMTP_PERMFAIL))
960 set_failure(ctx, RES_FAIL,
961 "unexpected response code2: %s", line);
962 break;
963 default:
964 set_failure(ctx, RES_FAIL,
965 "unexpected response code???: %s", line);
966 break;
967 }
968 break;
969
970 default:
971 ctx->result = RES_ERROR;
972 ctx->reason = "invalid operator";
973 break;
974 }
975 }
976
977 static const char *
parse_smtp_response(char * line,size_t len,char ** msg,int * cont)978 parse_smtp_response(char *line, size_t len, char **msg, int *cont)
979 {
980 size_t i;
981
982 if (len >= SMTP_LINE_MAX)
983 return "line too long";
984
985 if (len > 3) {
986 if (msg)
987 *msg = line + 4;
988 if (cont)
989 *cont = (line[3] == '-');
990 } else if (len == 3) {
991 if (msg)
992 *msg = line + 3;
993 if (cont)
994 *cont = 0;
995 } else
996 return "line too short";
997
998 /* validate reply code */
999 if (line[0] < '2' || line[0] > '5' || !isdigit(line[1]) ||
1000 !isdigit(line[2]))
1001 return "reply code out of range";
1002
1003 /* validate reply message */
1004 for (i = 0; i < len; i++)
1005 if (!isprint(line[i]))
1006 return "non-printable character in reply";
1007
1008 return NULL;
1009 }
1010