Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/appl/push/push.c
34869 views
1
/*
2
* Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
3
* (Royal Institute of Technology, Stockholm, Sweden).
4
* All rights reserved.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
*
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
*
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
*
17
* 3. Neither the name of the Institute nor the names of its contributors
18
* may be used to endorse or promote products derived from this software
19
* without specific prior written permission.
20
*
21
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31
* SUCH DAMAGE.
32
*/
33
34
#include "push_locl.h"
35
RCSID("$Id$");
36
37
#if defined(_AIX) && defined(STAT)
38
/*
39
* AIX defines STAT to 1 in sys/dir.h
40
*/
41
# undef STAT
42
#endif
43
44
#ifdef KRB5
45
static int use_v5 = -1;
46
static krb5_context context;
47
#endif
48
49
static char *port_str;
50
static int verbose_level;
51
static int do_fork;
52
static int do_leave;
53
static int do_version;
54
static int do_help;
55
static int do_from;
56
static int do_count;
57
static char *header_str;
58
59
struct getargs args[] = {
60
#ifdef KRB5
61
{ "krb5", '5', arg_flag, &use_v5, "Use Kerberos V5",
62
NULL },
63
#endif
64
{ "verbose",'v', arg_counter, &verbose_level, "Verbose",
65
NULL },
66
{ "fork", 'f', arg_flag, &do_fork, "Fork deleting proc",
67
NULL },
68
{ "leave", 'l', arg_flag, &do_leave, "Leave mail on server",
69
NULL },
70
{ "port", 'p', arg_string, &port_str, "Use this port",
71
"number-or-service" },
72
{ "from", 0, arg_flag, &do_from, "Behave like from",
73
NULL },
74
{ "headers", 0, arg_string, &header_str, "Headers to print", NULL },
75
{ "count", 'c', arg_flag, &do_count, "Print number of messages", NULL},
76
{ "version", 0, arg_flag, &do_version, "Print version",
77
NULL },
78
{ "help", 0, arg_flag, &do_help, NULL,
79
NULL }
80
81
};
82
83
static void
84
usage (int ret)
85
{
86
arg_printusage (args,
87
sizeof(args) / sizeof(args[0]),
88
NULL,
89
"[[{po:username[@hostname] | hostname[:username]}] ...] "
90
"filename");
91
exit (ret);
92
}
93
94
static int
95
do_connect (const char *hostname, int port, int nodelay)
96
{
97
struct addrinfo *ai, *a;
98
struct addrinfo hints;
99
int error;
100
int s = -1;
101
char portstr[NI_MAXSERV];
102
103
memset (&hints, 0, sizeof(hints));
104
hints.ai_socktype = SOCK_STREAM;
105
hints.ai_protocol = IPPROTO_TCP;
106
107
snprintf (portstr, sizeof(portstr), "%u", ntohs(port));
108
109
error = getaddrinfo (hostname, portstr, &hints, &ai);
110
if (error)
111
errx (1, "getaddrinfo(%s): %s", hostname, gai_strerror(error));
112
113
for (a = ai; a != NULL; a = a->ai_next) {
114
s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
115
if (s < 0)
116
continue;
117
if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
118
warn ("connect(%s)", hostname);
119
close (s);
120
continue;
121
}
122
break;
123
}
124
freeaddrinfo (ai);
125
if (a == NULL) {
126
warnx ("failed to contact %s", hostname);
127
return -1;
128
}
129
130
if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
131
(void *)&nodelay, sizeof(nodelay)) < 0)
132
err (1, "setsockopt TCP_NODELAY");
133
return s;
134
}
135
136
typedef enum { INIT = 0, GREET, USER, PASS, STAT, RETR, TOP,
137
DELE, XDELE, QUIT} pop_state;
138
139
static char *pop_state_string[] = {
140
"INIT", "GREET", "USER", "PASS", "STAT", "RETR", "TOP",
141
"DELE", "XDELE", "QUIT"
142
};
143
144
#define PUSH_BUFSIZ 65536
145
146
#define STEP 16
147
148
struct write_state {
149
struct iovec *iovecs;
150
size_t niovecs, maxiovecs, allociovecs;
151
int fd;
152
};
153
154
static void
155
write_state_init (struct write_state *w, int fd)
156
{
157
#ifdef UIO_MAXIOV
158
w->maxiovecs = UIO_MAXIOV;
159
#else
160
w->maxiovecs = 16;
161
#endif
162
w->allociovecs = min(STEP, w->maxiovecs);
163
w->niovecs = 0;
164
w->iovecs = emalloc(w->allociovecs * sizeof(*w->iovecs));
165
w->fd = fd;
166
}
167
168
static void
169
write_state_add (struct write_state *w, void *v, size_t len)
170
{
171
if(w->niovecs == w->allociovecs) {
172
if(w->niovecs == w->maxiovecs) {
173
if(writev (w->fd, w->iovecs, w->niovecs) < 0)
174
err(1, "writev");
175
w->niovecs = 0;
176
} else {
177
w->allociovecs = min(w->allociovecs + STEP, w->maxiovecs);
178
w->iovecs = erealloc (w->iovecs,
179
w->allociovecs * sizeof(*w->iovecs));
180
}
181
}
182
w->iovecs[w->niovecs].iov_base = v;
183
w->iovecs[w->niovecs].iov_len = len;
184
++w->niovecs;
185
}
186
187
static void
188
write_state_flush (struct write_state *w)
189
{
190
if (w->niovecs) {
191
if (writev (w->fd, w->iovecs, w->niovecs) < 0)
192
err (1, "writev");
193
w->niovecs = 0;
194
}
195
}
196
197
static void
198
write_state_destroy (struct write_state *w)
199
{
200
free (w->iovecs);
201
}
202
203
static int
204
doit(int s,
205
const char *host,
206
const char *user,
207
const char *outfilename,
208
const char *header_str,
209
int leavep,
210
int verbose,
211
int forkp)
212
{
213
int ret;
214
char out_buf[PUSH_BUFSIZ];
215
int out_len = 0;
216
char *in_buf;
217
size_t in_buf_size;
218
size_t in_len = 0;
219
char *in_ptr;
220
pop_state state = INIT;
221
unsigned count = 0, bytes;
222
unsigned asked_for = 0, retrieved = 0, asked_deleted = 0, deleted = 0;
223
unsigned sent_xdele = 0;
224
int out_fd;
225
char from_line[128];
226
size_t from_line_length;
227
time_t now;
228
struct write_state write_state;
229
unsigned int numheaders = 1;
230
char **headers = NULL;
231
int i;
232
char *tmp = NULL;
233
234
in_buf = emalloc(PUSH_BUFSIZ + 1);
235
in_ptr = in_buf;
236
in_buf_size = PUSH_BUFSIZ;
237
238
if (do_from) {
239
char *tmp2;
240
241
tmp2 = tmp = estrdup(header_str);
242
243
out_fd = -1;
244
if (verbose)
245
fprintf (stderr, "%s@%s\n", user, host);
246
while (*tmp != '\0') {
247
tmp = strchr(tmp, ',');
248
if (tmp == NULL)
249
break;
250
tmp++;
251
numheaders++;
252
}
253
254
headers = emalloc(sizeof(char *) * (numheaders + 1));
255
for (i = 0; i < numheaders; i++) {
256
headers[i] = strtok_r(tmp2, ",", &tmp2);
257
}
258
headers[numheaders] = NULL;
259
} else {
260
out_fd = open(outfilename, O_WRONLY | O_APPEND | O_CREAT, 0666);
261
if (out_fd < 0)
262
err (1, "open %s", outfilename);
263
if (verbose)
264
fprintf (stderr, "%s@%s -> %s\n", user, host, outfilename);
265
}
266
267
now = time(NULL);
268
from_line_length = snprintf (from_line, sizeof(from_line),
269
"From %s %s", "push", ctime(&now));
270
if (from_line_length < 0 || from_line_length > sizeof(from_line))
271
errx (1, "snprintf failed");
272
273
out_len = snprintf (out_buf, sizeof(out_buf),
274
"USER %s\r\nPASS hej\r\nSTAT\r\n",
275
user);
276
if (out_len < 0 || out_len > sizeof(out_buf))
277
errx (1, "snprintf failed");
278
if (net_write (s, out_buf, out_len) != out_len)
279
err (1, "write");
280
if (verbose > 1)
281
fprintf (stderr, "%s", out_buf);
282
283
if (!do_from)
284
write_state_init (&write_state, out_fd);
285
286
while(state != QUIT) {
287
fd_set readset, writeset;
288
289
FD_ZERO(&readset);
290
FD_ZERO(&writeset);
291
if (s >= FD_SETSIZE)
292
errx (1, "fd too large");
293
FD_SET(s,&readset);
294
295
if (verbose > 1)
296
fprintf (stderr, "state: %s count: %d asked_for: %d "
297
"retrieved: %d asked_deleted: %d\n",
298
pop_state_string[state],
299
count, asked_for, retrieved, asked_deleted);
300
301
if (((state == STAT || state == RETR || state == TOP)
302
&& asked_for < count)
303
|| (state == XDELE && !sent_xdele)
304
|| (state == DELE && asked_deleted < count))
305
FD_SET(s,&writeset);
306
ret = select (s + 1, &readset, &writeset, NULL, NULL);
307
if (ret < 0) {
308
if (errno == EAGAIN)
309
continue;
310
else
311
err (1, "select");
312
}
313
314
if (FD_ISSET(s, &readset)) {
315
char *beg, *p;
316
size_t rem;
317
int blank_line = 0;
318
319
if(in_len >= in_buf_size) {
320
char *tmp = erealloc(in_buf, in_buf_size + PUSH_BUFSIZ + 1);
321
in_ptr = tmp + (in_ptr - in_buf);
322
in_buf = tmp;
323
in_buf_size += PUSH_BUFSIZ;
324
}
325
326
ret = read (s, in_ptr, in_buf_size - in_len);
327
if (ret < 0)
328
err (1, "read");
329
else if (ret == 0)
330
errx (1, "EOF during read");
331
332
in_len += ret;
333
in_ptr += ret;
334
*in_ptr = '\0';
335
336
beg = in_buf;
337
rem = in_len;
338
while(rem > 1
339
&& (p = strstr(beg, "\r\n")) != NULL) {
340
if (state == TOP) {
341
char *copy = beg;
342
343
for (i = 0; i < numheaders; i++) {
344
size_t len;
345
346
len = min(p - copy + 1, strlen(headers[i]));
347
if (strncasecmp(copy, headers[i], len) == 0) {
348
fprintf (stdout, "%.*s\n", (int)(p - copy), copy);
349
}
350
}
351
if (beg[0] == '.' && beg[1] == '\r' && beg[2] == '\n') {
352
if (numheaders > 1)
353
fprintf (stdout, "\n");
354
state = STAT;
355
if (++retrieved == count) {
356
state = QUIT;
357
net_write (s, "QUIT\r\n", 6);
358
if (verbose > 1)
359
fprintf (stderr, "QUIT\r\n");
360
}
361
}
362
rem -= p - beg + 2;
363
beg = p + 2;
364
} else if (state == RETR) {
365
char *copy = beg;
366
if (beg[0] == '.') {
367
if (beg[1] == '\r' && beg[2] == '\n') {
368
if(!blank_line)
369
write_state_add(&write_state, "\n", 1);
370
state = STAT;
371
rem -= p - beg + 2;
372
beg = p + 2;
373
if (++retrieved == count) {
374
write_state_flush (&write_state);
375
if (fsync (out_fd) < 0)
376
err (1, "fsync");
377
close(out_fd);
378
if (leavep) {
379
state = QUIT;
380
net_write (s, "QUIT\r\n", 6);
381
if (verbose > 1)
382
fprintf (stderr, "QUIT\r\n");
383
} else {
384
if (forkp) {
385
pid_t pid;
386
387
pid = fork();
388
if (pid < 0)
389
warn ("fork");
390
else if(pid != 0) {
391
if(verbose)
392
fprintf (stderr,
393
"(exiting)");
394
return 0;
395
}
396
}
397
398
state = XDELE;
399
if (verbose)
400
fprintf (stderr, "deleting... ");
401
}
402
}
403
continue;
404
} else
405
++copy;
406
}
407
*p = '\n';
408
if(blank_line &&
409
strncmp(copy, "From ", min(p - copy + 1, 5)) == 0)
410
write_state_add(&write_state, ">", 1);
411
write_state_add(&write_state, copy, p - copy + 1);
412
blank_line = (*copy == '\n');
413
rem -= p - beg + 2;
414
beg = p + 2;
415
} else if (rem >= 3 && strncmp (beg, "+OK", 3) == 0) {
416
if (state == STAT) {
417
if (!do_from)
418
write_state_add(&write_state,
419
from_line, from_line_length);
420
blank_line = 0;
421
if (do_from)
422
state = TOP;
423
else
424
state = RETR;
425
} else if (state == XDELE) {
426
state = QUIT;
427
net_write (s, "QUIT\r\n", 6);
428
if (verbose > 1)
429
fprintf (stderr, "QUIT\r\n");
430
break;
431
} else if (state == DELE) {
432
if (++deleted == count) {
433
state = QUIT;
434
net_write (s, "QUIT\r\n", 6);
435
if (verbose > 1)
436
fprintf (stderr, "QUIT\r\n");
437
break;
438
}
439
} else if (++state == STAT) {
440
if(sscanf (beg + 4, "%u %u", &count, &bytes) != 2)
441
errx(1, "Bad STAT-line: %.*s", (int)(p - beg), beg);
442
if (verbose) {
443
fprintf (stderr, "%u message(s) (%u bytes). "
444
"fetching... ",
445
count, bytes);
446
if (do_from)
447
fprintf (stderr, "\n");
448
} else if (do_count) {
449
fprintf (stderr, "%u message(s) (%u bytes).\n",
450
count, bytes);
451
}
452
if (count == 0) {
453
state = QUIT;
454
net_write (s, "QUIT\r\n", 6);
455
if (verbose > 1)
456
fprintf (stderr, "QUIT\r\n");
457
break;
458
}
459
}
460
461
rem -= p - beg + 2;
462
beg = p + 2;
463
} else {
464
if(state == XDELE) {
465
state = DELE;
466
rem -= p - beg + 2;
467
beg = p + 2;
468
} else
469
errx (1, "Bad response: %.*s", (int)(p - beg), beg);
470
}
471
}
472
if (!do_from)
473
write_state_flush (&write_state);
474
475
memmove (in_buf, beg, rem);
476
in_len = rem;
477
in_ptr = in_buf + rem;
478
}
479
if (FD_ISSET(s, &writeset)) {
480
if ((state == STAT && !do_from) || state == RETR)
481
out_len = snprintf (out_buf, sizeof(out_buf),
482
"RETR %u\r\n", ++asked_for);
483
else if ((state == STAT && do_from) || state == TOP)
484
out_len = snprintf (out_buf, sizeof(out_buf),
485
"TOP %u 0\r\n", ++asked_for);
486
else if(state == XDELE) {
487
out_len = snprintf(out_buf, sizeof(out_buf),
488
"XDELE %u %u\r\n", 1, count);
489
sent_xdele++;
490
}
491
else if(state == DELE)
492
out_len = snprintf (out_buf, sizeof(out_buf),
493
"DELE %u\r\n", ++asked_deleted);
494
if (out_len < 0 || out_len > sizeof(out_buf))
495
errx (1, "snprintf failed");
496
if (net_write (s, out_buf, out_len) != out_len)
497
err (1, "write");
498
if (verbose > 1)
499
fprintf (stderr, "%s", out_buf);
500
}
501
}
502
if (verbose)
503
fprintf (stderr, "Done\n");
504
if (do_from) {
505
free (tmp);
506
free (headers);
507
} else {
508
write_state_destroy (&write_state);
509
}
510
return 0;
511
}
512
513
#ifdef KRB5
514
static int
515
do_v5 (const char *host,
516
int port,
517
const char *user,
518
const char *filename,
519
const char *header_str,
520
int leavep,
521
int verbose,
522
int forkp)
523
{
524
krb5_error_code ret;
525
krb5_auth_context auth_context = NULL;
526
krb5_principal server;
527
int s;
528
529
s = do_connect (host, port, 1);
530
if (s < 0)
531
return 1;
532
533
ret = krb5_sname_to_principal (context,
534
host,
535
"pop",
536
KRB5_NT_SRV_HST,
537
&server);
538
if (ret) {
539
warnx ("krb5_sname_to_principal: %s",
540
krb5_get_err_text (context, ret));
541
return 1;
542
}
543
544
ret = krb5_sendauth (context,
545
&auth_context,
546
&s,
547
"KPOPV1.0",
548
NULL,
549
server,
550
0,
551
NULL,
552
NULL,
553
NULL,
554
NULL,
555
NULL,
556
NULL);
557
krb5_free_principal (context, server);
558
if (ret) {
559
warnx ("krb5_sendauth: %s",
560
krb5_get_err_text (context, ret));
561
return 1;
562
}
563
return doit (s, host, user, filename, header_str, leavep, verbose, forkp);
564
}
565
#endif
566
567
#ifdef HESIOD
568
569
#ifdef HESIOD_INTERFACES
570
571
static char *
572
hesiod_get_pobox (const char **user)
573
{
574
void *context;
575
struct hesiod_postoffice *hpo;
576
char *ret = NULL;
577
578
if(hesiod_init (&context) != 0)
579
err (1, "hesiod_init");
580
581
hpo = hesiod_getmailhost (context, *user);
582
if (hpo == NULL) {
583
warn ("hesiod_getmailhost %s", *user);
584
} else {
585
if (strcasecmp(hpo->hesiod_po_type, "pop") != 0)
586
errx (1, "Unsupported po type %s", hpo->hesiod_po_type);
587
588
ret = estrdup(hpo->hesiod_po_host);
589
*user = estrdup(hpo->hesiod_po_name);
590
hesiod_free_postoffice (context, hpo);
591
}
592
hesiod_end (context);
593
return ret;
594
}
595
596
#else /* !HESIOD_INTERFACES */
597
598
static char *
599
hesiod_get_pobox (const char **user)
600
{
601
char *ret = NULL;
602
struct hes_postoffice *hpo;
603
604
hpo = hes_getmailhost (*user);
605
if (hpo == NULL) {
606
warn ("hes_getmailhost %s", *user);
607
} else {
608
if (strcasecmp(hpo->po_type, "pop") != 0)
609
errx (1, "Unsupported po type %s", hpo->po_type);
610
611
ret = estrdup(hpo->po_host);
612
*user = estrdup(hpo->po_name);
613
}
614
return ret;
615
}
616
617
#endif /* HESIOD_INTERFACES */
618
619
#endif /* HESIOD */
620
621
static char *
622
get_pobox (const char **user)
623
{
624
char *ret = NULL;
625
626
#ifdef HESIOD
627
ret = hesiod_get_pobox (user);
628
#endif
629
630
if (ret == NULL)
631
ret = getenv("MAILHOST");
632
if (ret == NULL)
633
errx (1, "MAILHOST not set");
634
return ret;
635
}
636
637
static void
638
parse_pobox (char *a0, const char **host, const char **user)
639
{
640
const char *h, *u;
641
char *p;
642
int po = 0;
643
644
if (a0 == NULL) {
645
646
*user = getenv ("USERNAME");
647
if (*user == NULL) {
648
struct passwd *pwd = getpwuid (getuid ());
649
650
if (pwd == NULL)
651
errx (1, "Who are you?");
652
*user = estrdup (pwd->pw_name);
653
}
654
*host = get_pobox (user);
655
return;
656
}
657
658
/* if the specification starts with po:, remember this information */
659
if(strncmp(a0, "po:", 3) == 0) {
660
a0 += 3;
661
po++;
662
}
663
/* if there is an `@', the hostname is after it, otherwise at the
664
beginning of the string */
665
p = strchr(a0, '@');
666
if(p != NULL) {
667
*p++ = '\0';
668
h = p;
669
} else {
670
h = a0;
671
}
672
/* if there is a `:', the username comes before it, otherwise at
673
the beginning of the string */
674
p = strchr(a0, ':');
675
if(p != NULL) {
676
*p++ = '\0';
677
u = p;
678
} else {
679
u = a0;
680
}
681
if(h == u) {
682
/* some inconsistent compatibility with various mailers */
683
if(po) {
684
h = get_pobox (&u);
685
} else {
686
u = get_default_username ();
687
if (u == NULL)
688
errx (1, "Who are you?");
689
}
690
}
691
*host = h;
692
*user = u;
693
}
694
695
int
696
main(int argc, char **argv)
697
{
698
int port = 0;
699
int optind = 0;
700
int ret = 1;
701
const char *host, *user, *filename = NULL;
702
char *pobox = NULL;
703
704
setprogname (argv[0]);
705
706
#ifdef KRB5
707
{
708
krb5_error_code ret;
709
710
ret = krb5_init_context (&context);
711
if (ret)
712
errx (1, "krb5_init_context failed: %d", ret);
713
}
714
#endif
715
716
if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
717
&optind))
718
usage (1);
719
720
argc -= optind;
721
argv += optind;
722
723
if (do_help)
724
usage (0);
725
726
if (do_version) {
727
print_version(NULL);
728
return 0;
729
}
730
731
if (do_from && header_str == NULL)
732
header_str = "From:";
733
else if (header_str != NULL)
734
do_from = 1;
735
736
if (do_from) {
737
if (argc == 0)
738
pobox = NULL;
739
else if (argc == 1)
740
pobox = argv[0];
741
else
742
usage (1);
743
} else {
744
if (argc == 1) {
745
filename = argv[0];
746
pobox = NULL;
747
} else if (argc == 2) {
748
filename = argv[1];
749
pobox = argv[0];
750
} else
751
usage (1);
752
}
753
754
if (port_str) {
755
struct servent *s = roken_getservbyname (port_str, "tcp");
756
757
if (s)
758
port = s->s_port;
759
else {
760
char *ptr;
761
762
port = strtol (port_str, &ptr, 10);
763
if (port == 0 && ptr == port_str)
764
errx (1, "Bad port `%s'", port_str);
765
port = htons(port);
766
}
767
}
768
if (port == 0) {
769
#ifdef KRB5
770
port = krb5_getportbyname (context, "kpop", "tcp", 1109);
771
#else
772
#error must define KRB5
773
#endif
774
}
775
776
parse_pobox (pobox, &host, &user);
777
778
#ifdef KRB5
779
if (ret && use_v5) {
780
ret = do_v5 (host, port, user, filename, header_str,
781
do_leave, verbose_level, do_fork);
782
}
783
#endif
784
return ret;
785
}
786
787