1 /*
2 ** NOTICE:
3 **
4 ** Portions of this file contain code based on ideas derived
5 ** from the source code of "sendmail" written by Eric Allman
6 ** at the University of California, Berkeley.
7 */
8 #include "qpage.h"
9
10 #ifdef TCP_WRAPPERS
11 #include "tcpd.h"
12 #endif
13
14
15 /*
16 ** Level 1 commands
17 */
18 #define CMDERROR 0 /* bad command */
19 #define CMDPAGE 1 /* specify a pagerid */
20 #define CMDMESS 2 /* specify the message text */
21 #define CMDRESE 3 /* reset all settings */
22 #define CMDSEND 4 /* send the page */
23 #define CMDQUIT 5 /* disconnect */
24 #define CMDHELP 6 /* send help information */
25
26 /*
27 ** Level 2 commands
28 */
29 #define CMDDATA 7 /* prompt for message text */
30 #define CMDLOGI 8 /* authenticate remote user */
31 #define CMDLEVE 9 /* specify service level */
32 #define CMDALER 10 /* alert (not implemented) */
33 #define CMDCOVE 11 /* specify alternate coverage */
34 #define CMDHOLD 12 /* hold page until some future time */
35 #define CMDCALL 13 /* specify callerid */
36 #define CMDSUBJ 14 /* subject (not implemented) */
37
38 /*
39 ** Level 3 commands (not implemented)
40 */
41 #define CMD2WAY 15 /* begin 2-way paging */
42 #define CMDPING 16 /* ping a pager */
43 #define CMDEXPT 17 /* change expiration time */
44 #define CMDNOQU 18 /* don't queue message */
45 #define CMDACKR 19 /* read acknowledgment */
46 #define CMDRTYP 20 /* reply type */
47 #define CMDMCRE 21 /* multiple choice responce codes */
48 #define CMDMSTA 22 /* message status */
49 #define CMDKTAG 23 /* kill message */
50
51 /*
52 ** Other commands which are not part of the SNPP protocol
53 **
54 ** Note: These commands are not documented, not supported, and may very
55 ** well disappear in a future release. They do not produce results
56 ** consistant with RFC-1861. Use at your own risk.
57 */
58 #define CMDXDEB 24 /* activate debug mode */
59 #define CMDXCON 25 /* print configuration file */
60 #define CMDXQUE 26 /* show the page queue contents */
61 #define CMDXWHO 27 /* show the known pagers/groups */
62
63
64 /*
65 ** global variables
66 */
67 #ifndef lint
68 static char sccsid[] = "@(#)srvrsnpp.c 1.46 10/24/98 tomiii@qpage.org";
69 #endif
70
71 #ifdef TCP_WRAPPERS
72 int allow_severity = LOG_INFO;
73 int deny_severity = LOG_WARNING;
74 #endif
75
76 static struct cmd {
77 char *cmdname;
78 int cmdcode;
79 } CmdTab[] = {
80 {"PAGEr", CMDPAGE},
81 {"MESSage", CMDMESS},
82 {"RESEt", CMDRESE},
83 {"SEND", CMDSEND},
84 {"QUIT", CMDQUIT},
85 {"HELP", CMDHELP},
86 {"DATA", CMDDATA},
87 {"LOGIn", CMDLOGI},
88 {"LEVEl", CMDLEVE},
89 {"ALERt", CMDALER},
90 {"COVErage", CMDCOVE},
91 {"HOLDuntil", CMDHOLD},
92 {"CALLerid", CMDCALL},
93 {"SUBJect", CMDSUBJ},
94 {"2WAY", CMD2WAY},
95 {"PING", CMDPING},
96 {"EXPTag", CMDEXPT},
97 {"NOQUEUEing", CMDNOQU},
98 {"ACKRead", CMDACKR},
99 {"RTYPe", CMDRTYP},
100 {"MCREsponse", CMDMCRE},
101 {"MSTAtus", CMDMSTA},
102 {"KTAG", CMDKTAG},
103 {"XDEBug", CMDXDEB},
104 {"XCONfig", CMDXCON},
105 {"XQUEue", CMDXQUE},
106 {"XWHO", CMDXWHO},
107 {NULL, CMDERROR},
108 };
109
110
111 /*
112 ** message()
113 **
114 ** This function sends a string followed by CRLF to the standard output.
115 **
116 ** Input:
117 ** text - the string to print
118 **
119 ** Returns:
120 ** nothing
121 */
122 void
message(char * text)123 message(char *text)
124 {
125 if (Debug) {
126 fprintf(stderr, ">>> %s\r\n", text);
127 (void)fflush(stderr);
128 }
129
130 printf("%s\r\n", text);
131 (void)fflush(stdout);
132 }
133
134
135 /*
136 ** clear_page()
137 **
138 ** This function frees the memory allocated to a page during the
139 ** SNPP session. Memory allocated before the session started (such
140 ** as the socket's file descriptor) may or may not be freed,
141 ** depending on the save parameter.
142 **
143 ** Input:
144 ** p - a pointer to the PAGE structure
145 ** save - whether to save certain fields
146 **
147 ** Returns:
148 ** nothing
149 */
150 void
clear_page(PAGE * p,int save)151 clear_page(PAGE *p, int save)
152 {
153 FILE *peer;
154 rcpt_t *tmp;
155 char *ident;
156 char *msgid;
157 char *hostname;
158
159
160 /*
161 ** free all the recipients
162 */
163 while (p->rcpts) {
164 tmp = p->rcpts;
165 p->rcpts = p->rcpts->next;
166
167 my_free(tmp->pager);
168 my_free(tmp->coverage);
169 free(tmp);
170 }
171
172 if (save) {
173 /*
174 ** save a few things
175 */
176 peer = p->peer;
177 ident = p->ident;
178 msgid = p->messageid;
179 hostname = p->hostname;
180 }
181
182 /*
183 ** free the page
184 */
185 my_free(p->filename);
186 my_free(p->message);
187 my_free(p->auth);
188 my_free(p->from);
189 my_free(p->status);
190
191 if (save) {
192 (void)memset((char *)p, 0, sizeof(*p));
193
194 /*
195 ** restore the saved fields
196 */
197 p->peer = peer;
198 p->ident = ident;
199 p->messageid = msgid;
200 p->hostname = hostname;
201 }
202 else
203 free(p);
204 }
205
206
207 /*
208 ** newmsgid()
209 **
210 ** This function generates a unique message id. Note that the client
211 ** is not limited to one page per connection, so this message id will
212 ** have to be updated later on after the SEND command is received.
213 **
214 ** Input:
215 ** buf - where to store the new id (must hold >7 characters)
216 **
217 ** Returns:
218 ** nothing
219 **
220 ** Note:
221 ** This assumes we'll never have more than 1000 incoming
222 ** SNPP connections per minute. Probably a fair assumption,
223 ** but if this ever happens we'll end up with duplicate
224 ** message IDs.
225 */
226 void
newmsgid(char * buf)227 newmsgid(char *buf)
228 {
229 static char idchars[] =
230 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
231 "abcdefghijklmnopqrstuvwxyz"
232 "0123456789";
233
234 static int count;
235 struct tm *tm;
236 time_t now;
237
238
239 now = time(NULL);
240
241 /*
242 ** use gmtime() so time never goes backwards
243 */
244 tm = gmtime(&now);
245
246 buf[0] = idchars[tm->tm_mon];
247 buf[1] = idchars[tm->tm_mday-1];
248 buf[2] = idchars[tm->tm_hour];
249 buf[3] = idchars[tm->tm_min];
250
251 /*
252 ** make it unique
253 */
254 (void)sprintf(&buf[4], "%03d", count++);
255 count %= 1000;
256 }
257
258
259 /*
260 ** dohelp()
261 **
262 ** This function prints the list of SNPP commands supported by this program.
263 */
264 void
dohelp(void)265 dohelp(void)
266 {
267 message("214 ");
268 message("214 Level 1 commands accepted:");
269 message("214 ");
270 message("214 PAGEr <pager ID>");
271 message("214 MESSage <alpha or numeric message>");
272 message("214 RESEt");
273 message("214 SEND");
274 message("214 QUIT");
275 message("214 HELP");
276 message("214 ");
277 message("214 Level 2 commands accepted:");
278 message("214 ");
279 message("214 DATA");
280 message("214 LOGIn <loginid> [password]");
281 message("214 LEVEl <ServiceLevel>");
282 message("214 COVErage <AlternateArea>");
283 message("214 HOLDuntil <YYMMDDHHMMSS> [+/-GMTdifference]");
284 message("214 CALLerid <CallerID>");
285 message("214 ");
286 message("214 Level 3 commands accepted:");
287 message("214 ");
288 message("214 none");
289 message("214 ");
290 message("250 OK");
291 }
292
293
294 /*
295 ** dump_pagers()
296 **
297 ** This function prints the list of pagers/groups known by this server.
298 ** Only those with text descriptions are printed.
299 */
300 void
dump_pagers(void)301 dump_pagers(void)
302 {
303 pager_t *pager;
304 pgroup_t *group;
305 char buff[1024];
306
307
308 for (pager=Pagers; pager; pager=pager->next) {
309 if (pager->text == NULL)
310 continue;
311
312 (void)sprintf(buff, "214 %s %s", pager->name, pager->text);
313 message(buff);
314 }
315
316 for (group=Groups; group; group=group->next) {
317 if (group->text == NULL)
318 continue;
319
320 (void)sprintf(buff, "214 %s %s", group->name, group->text);
321 message(buff);
322 }
323 }
324
325
326 /*
327 ** authorize()
328 **
329 ** This function verifies a userid/password supplied as arguments
330 ** to the SNPP LOGIN command.
331 **
332 ** Input:
333 ** str - the userid and password, separated by whitespace
334 **
335 ** Returns:
336 ** the userid if verification succeeds, NULL if it doesn't
337 */
338 char *
authorize(char * str)339 authorize(char *str)
340 {
341 /*
342 ** add site-specific code here for authorization
343 */
344
345 return(strdup(str));
346 }
347
348
349 /*
350 ** find_recipients()
351 **
352 ** This function adds a pager to the page's recipient list. If the pager
353 ** specifies a page group, the group is expanded and each member is added
354 ** to the recipient list.
355 **
356 ** Input:
357 ** p - the page structure
358 ** str - the pager to be added
359 ** service - the default paging service
360 ** holduntil - the default hold time
361 ** level - the default service level
362 **
363 ** Returns:
364 ** an integer status code (0=success, 1=no one on duty, -1=error)
365 */
366 static int
find_recipients(PAGE * p,char * str,service_t * service,time_t holduntil,int level)367 find_recipients(PAGE *p, char *str, service_t *service, time_t holduntil, int level)
368 {
369 pgroup_t *group;
370 member_t *member;
371 rcpt_t *tmp;
372 char *ptr;
373 int flags;
374
375
376 tmp = NULL;
377 flags = 0;
378
379 /*
380 ** interpret the service level
381 */
382 switch (level) {
383 case LEVEL_SENDMAIL:
384 flags |= F_SENDMAIL;
385 break;
386 }
387
388 /*
389 ** see if they specified a group name
390 */
391 if ((group = lookup(Groups, str)) != NULL) {
392 for (member=group->members; member; member=member->next) {
393 /*
394 ** Check the time range for this member to make
395 ** sure he/she is "on-duty" when the page goes out.
396 */
397 if (!on_duty(member->schedule, holduntil)) {
398 if (Debug)
399 qpage_log(LOG_DEBUG,
400 "skipping %s (off-duty)",
401 member->pager->name);
402
403 continue;
404 }
405
406 /*
407 ** Members can be listed in a group more than once
408 ** if they are on-duty during different times of
409 ** the week. Check to make sure that this recipient
410 ** isn't already on the list (i.e. time ranges
411 ** overlap).
412 */
413 for (tmp=p->rcpts; tmp; tmp=tmp->next) {
414 if (!strcmp(member->pager->name, tmp->pager)) {
415 member = NULL;
416 break;
417 }
418 }
419
420 if (member == NULL)
421 continue;
422
423 tmp = (void *)malloc(sizeof(*tmp));
424 (void)memset((char *)tmp, 0, sizeof(*tmp));
425 tmp->pager = strdup(member->pager->name);
426 tmp->holduntil = holduntil;
427
428 if (service)
429 tmp->coverage = strdup(service->name);
430
431 tmp->level = level;
432 tmp->next = p->rcpts;
433 tmp->flags = flags;
434 p->rcpts = tmp;
435 }
436
437 /*
438 ** Return success if we found one or more valid recipients.
439 */
440 if (tmp)
441 return(0);
442 else
443 return(1);
444 }
445
446 /*
447 ** it wasn't a group name, try an individual pager
448 */
449 if (lookup(Pagers, str) == NULL) {
450 /*
451 ** The pager doesn't exist. Let's
452 ** see if they gave us a pagerid
453 ** rather than a username.
454 */
455 for (ptr=str; *ptr; ptr++)
456 if (!isdigit(*ptr))
457 return(-1);
458
459 if (*ptr)
460 return(-1);
461
462 if (service == NULL) {
463 service = lookup(Services,
464 "default");
465
466 if (service == NULL)
467 return(-1);
468 }
469
470 if (service->allowpid == FALSE)
471 return(-1);
472
473 flags |= F_RAWPID;
474 }
475
476 tmp = (void *)malloc(sizeof(*tmp));
477 (void)memset((char *)tmp, 0, sizeof(*tmp));
478 tmp->pager = strdup(str);
479 tmp->holduntil = holduntil;
480
481 if (service)
482 tmp->coverage = strdup(service->name);
483
484 tmp->level = level;
485 tmp->next = p->rcpts;
486 tmp->flags = flags;
487 p->rcpts = tmp;
488
489
490 return(0);
491 }
492
493
494 /*
495 ** snpp()
496 **
497 ** This function does most all the communication between
498 ** the client and the server.
499 **
500 ** Input:
501 ** p - an empty PAGE structure
502 **
503 ** Returns:
504 ** 0 on success, -1 on error
505 **
506 ** Note:
507 ** Calls to qpage_log() from within this function should
508 ** be treated as though XDEBug never existed.
509 */
510 int
snpp(PAGE * p)511 snpp(PAGE *p)
512 {
513 struct cmd *c;
514 service_t *service;
515 time_t holduntil;
516 time_t now;
517 job_t *joblist;
518 #ifdef DEBUG
519 FILE *fp;
520 #endif
521 char buff[1024];
522 char *cmdbuf;
523 char *errmsg;
524 char *a;
525 char *b;
526 char *m;
527 int i;
528 int badarg;
529 int gotpager;
530 int gotmessage;
531 int badcommands;
532 int level;
533 int pagecount;
534
535
536 service = NULL;
537 gotpager = 0;
538 holduntil = 0;
539 gotmessage = 0;
540 badcommands = 0;
541 pagecount = 0;
542 level = DEFAULT_LEVEL;
543
544 now = time(NULL);
545 (void)sprintf(buff, "220 QuickPage v%s SNPP server ready at %s",
546 VERSION, my_ctime(&now));
547
548 message(buff);
549
550 for (;;) {
551 while ((cmdbuf = getinput(p->peer, TRUE)) == NULL) {
552 if (++badcommands > MAXBADCOMMANDS) {
553 message("421 Too many errors, goodbye");
554 return(-1);
555 }
556
557 message("500 Command unrecognized");
558 }
559
560 /*
561 ** check for a timeout condition
562 */
563 if (*cmdbuf == '\0') {
564 message("421 Timeout, goodbye");
565 return(-1);
566 }
567
568 if (Debug) {
569 fprintf(stderr, "--> %s\n", cmdbuf);
570 (void)fflush(stderr);
571 }
572
573 /*
574 ** figure out which command they gave us
575 */
576 for (c=CmdTab; c->cmdname != NULL; c++) {
577 if (strncasecmp(c->cmdname, cmdbuf, 4) == 0)
578 break;
579 }
580
581 /*
582 ** find the first argument (if any)
583 */
584 a = cmdbuf;
585 while (*a && !isspace(*a))
586 a++;
587
588 /*
589 ** nuke leading whitespace from the argument list
590 */
591 while (*a && isspace(*a))
592 a++;
593
594 /*
595 ** process the command
596 */
597 switch (c->cmdcode) {
598 /*
599 ** Level 1 commands
600 */
601 case CMDPAGE:
602 errmsg = "550 Error, invalid pager ID";
603
604 if (*a == '\0') {
605 message(errmsg);
606 break;
607 }
608
609 /*
610 ** find the end of the argument
611 */
612 for (b=a; *b && !isspace(*b); b++)
613 continue;
614
615 *b = '\0';
616 while (*b && isspace(*b))
617 b++;
618
619 /*
620 ** We don't support the level 2 syntax yet
621 */
622 if (*b) {
623 message(errmsg);
624 break;
625 }
626
627 i = find_recipients(p, a, service, holduntil,
628 level);
629
630 if (i == 1)
631 errmsg = "550 Error, no group members on duty";
632
633 if (i) {
634 message(errmsg);
635 break;
636 }
637
638 gotpager++;
639
640 if (holduntil)
641 message("250 Pager ID accepted, message will be delayed");
642 else
643 message("250 Pager ID accepted, message will not be delayed");
644
645 /*
646 ** set the per-pager options to their defaults
647 */
648 service = NULL;
649 holduntil = 0;
650 level = DEFAULT_LEVEL;
651 break;
652
653 case CMDMESS:
654 if (gotmessage) {
655 message("503 Error, message already entered");
656 break;
657 }
658
659 if (*a == '\0') {
660 message("550 Empty message not allowed");
661 break;
662 }
663
664 /*
665 ** trim trailing whitespace
666 */
667 b = &a[strlen(a)-1];
668 while (b > a && isspace(*b))
669 *b-- = '\0';
670
671 p->message = strdup(a);
672 strip(&p->message);
673 gotmessage++;
674
675 message("250 Message ok");
676 break;
677
678 case CMDRESE:
679 clear_page(p, TRUE);
680 service = NULL;
681 gotpager = 0;
682 holduntil = 0;
683 gotmessage = 0;
684 badcommands = 0;
685 level = DEFAULT_LEVEL;
686
687 message("250 Reset ok");
688 break;
689
690 case CMDSEND:
691 if (!gotpager) {
692 message("503 Error, no pager ID");
693 clear_page(p, TRUE);
694 break;
695 }
696
697 if (!gotmessage) {
698 message("503 Error, no message");
699 clear_page(p, TRUE);
700 break;
701 }
702
703 p->created = time(NULL);
704 (void)sprintf(buff, "%d", pagecount++);
705 m = (void *)malloc(sizeof(*m) * (strlen(p->messageid) + strlen(buff) + 1));
706 if ( m == NULL ) {
707 message("554 Message failed (out of memory)");
708 qpage_log(LOG_ERR, "snpp(): cannot allocate memory for p->messageid");
709 clear_page(p, TRUE);
710 break;
711 }
712 (void)sprintf(m, "%s%s", p->messageid, buff);
713 my_free(p->messageid);
714 p->messageid = m;
715
716 qpage_log(LOG_ALERT, "page submitted, id=%s, from=%s",
717 p->messageid,
718 p->from ? p->from : "[anonymous]");
719
720 /*
721 ** If the XDEBug command was issued, send
722 ** the page out now rather than queueing it.
723 ** Set the interactive flag so the remote
724 ** user can see what is going on.
725 **
726 ** KLUDGE ALERT--we're sucking down memory
727 ** here without ever giving it back. Good
728 ** thing this is a separate process that
729 ** will exit soon.
730 */
731 if (Interactive) {
732 message("214 Sending message");
733 joblist = NULL;
734 (void)insert_jobs(&joblist, p);
735 send_pages(joblist);
736 message("250 Done sending message");
737 clear_page(p, TRUE);
738 break;
739 }
740
741 if (write_page(p, TRUE) < 0) {
742 message("554 Message failed (error writing queue file)");
743 qpage_log(LOG_ALERT, "write_page() failed for id=%s", p->messageid);
744 clear_page(p, TRUE);
745 break;
746 }
747
748 /*
749 ** Tell the parent there's work to do.
750 */
751 if (Synchronous)
752 (void)kill(getppid(), SIGUSR1);
753
754 clear_page(p, TRUE);
755 service = NULL;
756 gotpager = 0;
757 holduntil = 0;
758 gotmessage = 0;
759 badcommands = 0;
760 level = DEFAULT_LEVEL;
761
762 (void)sprintf(buff,
763 "250 Message %s queued for processing",
764 p->messageid);
765
766 message(buff);
767 break;
768
769 case CMDQUIT:
770 message("221 OK, goodbye");
771 return(0);
772
773 case CMDHELP:
774 dohelp();
775 break;
776
777 /*
778 ** Level 2 commands
779 */
780 case CMDDATA:
781 if (gotmessage) {
782 message("503 Error, message already entered");
783 break;
784 }
785
786 message("354 Begin input; end with <CRLF>'.'<CRLF>");
787
788 p->message = getinput(p->peer, FALSE);
789
790 if (p->message == NULL) {
791 message("550 Empty message not allowed");
792 break;
793 }
794
795 /*
796 ** check for a timeout condition
797 */
798 if (p->message[0] == '\0') {
799 message("421 Timeout, goodbye");
800 return(-1);
801 }
802
803 strip(&p->message);
804 gotmessage++;
805
806 message("250 Message ok");
807 break;
808
809 case CMDLOGI:
810 errmsg = "550 Error, invalid login or password";
811
812 if ((p->auth = authorize(a)) == NULL) {
813 message(errmsg);
814 break;
815 }
816
817 message("250 Login accepted");
818 break;
819
820 case CMDLEVE:
821 errmsg = "550 Error, invalid service level";
822
823 if (*a == '\0') {
824 message(errmsg);
825 break;
826 }
827
828 badarg = 0;
829 for (b=a; *b && !isspace(*b); b++) {
830 if (!isdigit(*b)) {
831 badarg++;
832 break;
833 }
834 }
835
836 *b = '\0';
837 while (*b && isspace(*b))
838 b++;
839
840 if (*b)
841 badarg++;
842
843 i = atoi(a);
844
845 if ((i < 0) || (i > 11))
846 badarg++;
847
848 if (badarg) {
849 message(errmsg);
850 break;
851 }
852
853 level = i;
854 message("250 OK, alternate service level accepted");
855 break;
856
857 case CMDALER:
858 message("500 Command not implemented");
859 break;
860
861 case CMDCOVE:
862 errmsg = "550 Error, invalid alternate region";
863
864 if (*a == '\0') {
865 message(errmsg);
866 break;
867 }
868
869 /*
870 ** find the end of the argument
871 */
872 for (b=a; *b && !isspace(*b); b++)
873 continue;
874
875 *b = '\0';
876 while (*b && isspace(*b))
877 b++;
878
879 if (*b) {
880 message(errmsg);
881 break;
882 }
883
884 if ((service = lookup(Services, a)) == NULL) {
885 message(errmsg);
886 break;
887 }
888
889 message("250 Alternate coverage selected");
890 break;
891
892 case CMDHOLD:
893 if ((holduntil = snpptime(a)) == INVALID_TIME) {
894 message("550 Error, invalid delivery date/time");
895 holduntil = 0;
896 break;
897 }
898
899 if ((b = my_ctime(&holduntil)) == NULL) {
900 message("554 ctime() failed");
901 break;
902 }
903
904 (void)sprintf(buff, "250 Message for next PAGEr will be delayed until %s", b);
905 message(buff);
906 break;
907
908 case CMDCALL:
909 if (*a == '\0') {
910 message("550 Error, invalid caller ID");
911 break;
912 }
913
914 /*
915 ** trim trailing whitespace
916 */
917 b = &a[strlen(a)-1];
918 while (b > a && isspace(*b))
919 *b-- = '\0';
920
921 my_free(p->from);
922 p->from = strdup(a);
923
924 #ifndef NOIDENT
925 if (p->ident && strcasecmp(p->ident, p->from))
926 message("250 You're a liar, but I'll trust you this time");
927 else
928 #endif
929 message("250 Caller ID accepted");
930 break;
931
932 case CMDSUBJ:
933 message("500 Command not implemented");
934 break;
935
936 /*
937 ** Level 3 commands
938 */
939 case CMD2WAY:
940 case CMDPING:
941 case CMDEXPT:
942 case CMDNOQU:
943 case CMDACKR:
944 case CMDRTYP:
945 case CMDMCRE:
946 case CMDMSTA:
947 case CMDKTAG:
948 message("500 Command not implemented");
949 break;
950
951 #ifdef DEBUG
952 /*
953 ** Other commands not part of SNPP protocol
954 */
955 case CMDXDEB:
956 qpage_log(LOG_ALERT, "debug mode entered");
957 Debug = TRUE;
958 Interactive = TRUE;
959 setbuf(stdout, NULL);
960 message("250 Debug mode entered");
961 break;
962
963 case CMDXCON:
964 qpage_log(LOG_ALERT, "configuration file requested");
965
966 dump_qpage_config(ConfigFile);
967
968 (void)fflush(stdout);
969 (void)fclose(fp);
970
971 message("250 Command complete");
972 break;
973
974 case CMDXQUE:
975 qpage_log(LOG_ALERT, "page queue requested");
976
977 printf("---------- start queue ----------\n");
978 i = showqueue();
979 printf("---------- end queue ----------\n");
980 (void)fflush(stdout);
981
982 message("250 Command complete");
983 break;
984 #endif /* DEBUG */
985
986 case CMDXWHO:
987 qpage_log(LOG_ALERT, "pagerid list requested");
988 dump_pagers();
989 message("250 Command complete");
990 break;
991
992 case CMDERROR:
993 default:
994 if (++badcommands > MAXBADCOMMANDS) {
995 message("421 Too many errors, goodbye");
996 return(-1);
997 }
998
999 message("500 Command unrecognized");
1000 break;
1001 }
1002 }
1003 }
1004
1005
1006 void
accept_connection(int sock)1007 accept_connection(int sock)
1008 {
1009 #ifdef TCP_WRAPPERS
1010 struct request_info request;
1011 char *ptr;
1012 #endif
1013 struct sockaddr_in addr;
1014 struct hostent *hp;
1015 pid_t pid;
1016 FILE *in;
1017 FILE *out;
1018 PAGE *p;
1019 char msgid[100];
1020 int len;
1021 int s;
1022
1023
1024 len = sizeof(addr);
1025 if ((s = accept(sock, (struct sockaddr *)&addr, &len)) < 0) {
1026 #ifdef ERESTART
1027 if (errno != EINTR && errno != ERESTART)
1028 #else
1029 if (errno != EINTR)
1030 #endif
1031 qpage_log(LOG_ERR, "accept() failed: %s",
1032 strerror(errno));
1033
1034 return;
1035 }
1036
1037 /*
1038 ** Generate a new message ID before the fork() to ensure
1039 ** that it will be somewhat unique.
1040 */
1041 newmsgid(msgid);
1042
1043 pid = fork();
1044
1045 if (pid != 0) {
1046 if (pid < 0)
1047 qpage_log(LOG_ERR, "fork() failed: %s",
1048 strerror(errno));
1049
1050 (void)close(s);
1051 return;
1052 }
1053
1054 /* CHILD */
1055
1056 /*
1057 ** create a new page structure
1058 */
1059 p = (void *)malloc(sizeof(*p));
1060 (void)memset((char *)p, 0, sizeof(*p));
1061 p->messageid = strdup(msgid);
1062
1063 #ifdef TCP_WRAPPERS
1064 if (request_init(&request, RQ_DAEMON, PROGRAM_NAME, RQ_FILE,
1065 s, NULL) != NULL) {
1066
1067 fromhost(&request);
1068
1069 ptr = eval_user(&request);
1070
1071 if (ptr && strcmp(ptr, STRING_UNKNOWN) != 0)
1072 p->ident = strdup(ptr);
1073
1074 ptr = eval_hostinfo(&request.client);
1075
1076 if (ptr && strcmp(ptr, STRING_UNKNOWN) != 0)
1077 p->hostname = strdup(ptr);
1078 }
1079
1080 if (!hosts_access(&request)) {
1081 ptr = "421 Permission Denied by Administrator\r\n";
1082 (void)write(s, ptr, strlen(ptr));
1083 (void)close(s);
1084
1085 qpage_log(LOG_INFO, "connection refused from %s@%s",
1086 p->ident ? p->ident : "[anonymous]", p->hostname);
1087 _exit(2);
1088 }
1089 #endif
1090
1091 if (p->hostname == NULL) {
1092 /*
1093 ** figure out where this connection came from
1094 */
1095 hp = gethostbyaddr((char *)&addr.sin_addr.s_addr,
1096 sizeof(addr.sin_addr.s_addr), AF_INET);
1097
1098 if (hp == NULL) {
1099 /*
1100 ** allocate enough room for [nnn.nnn.nnn.nnn]
1101 */
1102 p->hostname = (void *)malloc(17+1);
1103 (void)sprintf(p->hostname, "[%s]",
1104 inet_ntoa(addr.sin_addr));
1105 }
1106 else
1107 p->hostname = strdup(hp->h_name);
1108 }
1109
1110 if (IdentTimeout && p->ident == NULL)
1111 p->ident = ident(s);
1112
1113 qpage_log(LOG_INFO, "connection from %s@%s",
1114 p->ident ? p->ident : "[anonymous]", p->hostname);
1115
1116
1117 if ((in = fdopen(s, "r")) == NULL) {
1118 qpage_log(LOG_ERR, "cannot reopen socket");
1119 _exit(-1);
1120 }
1121
1122 if ((out = fdopen(dup(s), "w")) == NULL) {
1123 qpage_log(LOG_ERR, "cannot reopen socket");
1124 _exit(-1);
1125 }
1126
1127 /*
1128 ** stdout goes to the socket
1129 */
1130 if (fileno(out) != fileno(stdout))
1131 (void)dup2(fileno(out), fileno(stdout));
1132
1133 p->peer = in;
1134 (void)snpp(p);
1135
1136 qpage_log(LOG_INFO, "disconnect from %s@%s",
1137 p->ident ? p->ident : "[anonymous]", p->hostname);
1138
1139 (void)fclose(stdout);
1140 (void)fclose(out);
1141 (void)fclose(in);
1142 (void)close(s);
1143 _exit(0);
1144 }
1145
1146
1147 /*
1148 ** become_daemon()
1149 **
1150 ** This function opens a socket on the SNPP port and waits for
1151 ** incoming connections. When a connection is established, this
1152 ** function forks. The parent continues to listen for incoming
1153 ** connections while the child is handed off to snpp().
1154 **
1155 ** A background process is also started which continually processes
1156 ** the page queue. If there are jobs remaining in the queue after
1157 ** processing (such as jobs scheduled to run in the future), the
1158 ** background process waits for a specified number of seconds and
1159 ** starts over again. Otherwise, the background process goes to
1160 ** sleep until a new job is submitted to the queue.
1161 **
1162 ** Input:
1163 ** sleeptime - the time interval to sleep between iterations
1164 **
1165 ** Returns:
1166 ** -1 on error, otherwise never
1167 */
1168 int
become_daemon(int sleeptime,short port)1169 become_daemon(int sleeptime, short port)
1170 {
1171 struct sockaddr_in addr;
1172 struct servent *svc;
1173 struct timeval timeout;
1174 #ifdef HAVE_POLL
1175 struct pollfd fds;
1176 #else
1177 fd_set readfds;
1178 #endif
1179 time_t lastrun;
1180 time_t now;
1181 pid_t childpid;
1182 char pid[30];
1183 int dontsend;
1184 int sock;
1185 int len;
1186 int on;
1187 int fd;
1188 int i;
1189
1190
1191 if (sleeptime < 0) {
1192 dontsend = TRUE;
1193 sleeptime = 0;
1194 }
1195 else
1196 dontsend = FALSE;
1197
1198 lastrun = 0;
1199 childpid = 0;
1200
1201 if (port == 0) {
1202 /*
1203 ** Get the SNPP port number from the /etc/services map.
1204 ** Note that the port number is returned in network byte
1205 ** order so no call to htons() is required.
1206 */
1207 if ((svc = getservbyname("snpp", "tcp")) != NULL)
1208 port = svc->s_port;
1209 else
1210
1211 port = htons(SNPP_SVC_PORT);
1212 }
1213 else
1214 port = htons(port);
1215
1216 /*
1217 ** Make sure we have permission to bind to this port.
1218 */
1219 if (ntohs(port) < 1024 && geteuid() != 0)
1220 fprintf(stderr, "Warning: daemon must be started as root\n");
1221
1222 if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
1223 fprintf(stderr, "socket() failed: %s\n", strerror(errno));
1224 return(-1);
1225 }
1226
1227 /*
1228 ** Attempt to set REUSEADDR but it's no big deal if this fails.
1229 */
1230 on = 1;
1231 len = sizeof(on);
1232 (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, len);
1233
1234 addr.sin_addr.s_addr = INADDR_ANY;
1235 addr.sin_family = AF_INET;
1236 addr.sin_port = port;
1237
1238 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
1239 fprintf(stderr, "bind() failed: %s\n", strerror(errno));
1240 (void)close(sock);
1241 return(-1);
1242 }
1243
1244 if (listen(sock, 5) < 0) {
1245 fprintf(stderr, "listen() failed: %s\n", strerror(errno));
1246 (void)close(sock);
1247 return(-1);
1248 }
1249
1250 /*
1251 ** Give up all root permissions now that we're bound to the SNPP port
1252 */
1253 drop_root_privileges();
1254
1255 if (get_qpage_config(ConfigFile) != 0) {
1256 fprintf(stderr, "Error reading configuration file\n");
1257 return(-1);
1258 }
1259
1260 if (!Debug) {
1261 /*
1262 ** Detatch ourselves from the controling tty.
1263 */
1264 switch (fork()) {
1265 case (pid_t)-1:
1266 qpage_log(LOG_ERR, "fork() failed: %s",
1267 strerror(errno));
1268 return(-1);
1269
1270 case 0:
1271 /*
1272 ** Ensure that we will never have a
1273 ** controlling terminal.
1274 */
1275 (void)close(0);
1276 (void)close(1);
1277 (void)close(2);
1278 (void)setsid();
1279 if (fork())
1280 _exit(-1);
1281 break;
1282
1283 default:
1284 _exit(0);
1285 }
1286 }
1287
1288 /*
1289 ** Attempt to write our PID to a file specified by the
1290 ** administrator. Note that we do not know the filename
1291 ** until after we've read the configuration file and we
1292 ** can't read the configuration file until after we've
1293 ** dropped our root permissions (because otherwise the
1294 ** access() calls in config.c will be using the wrong
1295 ** permissions). Therefore, it is safe to use O_CREAT
1296 ** and O_TRUNC because we know it's not possible for
1297 ** someone to trick us into doing the wrong thing by
1298 ** feeding us a symbolic link.
1299 **
1300 ** The obvious downside here is that the file will most
1301 ** likely want to be in some system directory such as /etc,
1302 ** which means if the file doesn't already exist then the
1303 ** O_CREAT flag probably won't do us any good. Such is
1304 ** life, I guess.
1305 */
1306 if (PIDfile) {
1307 /*
1308 ** let the current umask determine the default permissions
1309 */
1310 fd = open(PIDfile, O_WRONLY|O_CREAT|O_TRUNC, 0666);
1311
1312 if (fd >= 0) {
1313 (void)sprintf(pid, "%d\n", (int)getpid());
1314 (void)write(fd, pid, strlen(pid));
1315 (void)close(fd);
1316 }
1317 }
1318
1319 if (Debug)
1320 qpage_log(LOG_DEBUG, "waiting for incoming connections");
1321
1322 /*
1323 ** spin forever, waiting for connections and processing the queue
1324 */
1325 for (;;) {
1326 timeout.tv_sec = sleeptime;
1327 timeout.tv_usec = 0;
1328
1329 if (JobsPending)
1330 timeout.tv_sec = 0;
1331
1332 #ifdef HAVE_POLL
1333 fds.fd = sock;
1334 fds.events = POLLIN;
1335 fds.revents = 0;
1336
1337 i = poll(&fds, 1, timeout.tv_sec * 1000);
1338 #else
1339 FD_ZERO_LINTED(&readfds);
1340 FD_SET(sock, &readfds);
1341
1342 i = select(FD_SETSIZE, &readfds, 0, 0,
1343 sleeptime ? &timeout : NULL);
1344 #endif
1345
1346 if (i < 0) {
1347 #ifdef ERESTART
1348 if (errno != EINTR && errno != ERESTART)
1349 #else
1350 if (errno != EINTR)
1351 #endif
1352 #ifdef HAVE_POLL
1353 qpage_log(LOG_ERR, "poll() failed: %s",
1354 strerror(errno));
1355 #else
1356 qpage_log(LOG_ERR, "select() failed: %s",
1357 strerror(errno));
1358 #endif
1359 }
1360
1361 if (ReReadConfig) {
1362 ReReadConfig = FALSE;
1363 qpage_log(LOG_NOTICE, "rereading configuration");
1364
1365 if (get_qpage_config(ConfigFile)) {
1366 qpage_log(LOG_NOTICE,
1367 "new configuration has errors");
1368 }
1369 }
1370
1371 #ifdef HAVE_POLL
1372 if (fds.revents & POLLIN)
1373 accept_connection(sock);
1374 #else
1375 if (i > 0 && FD_ISSET(sock, &readfds))
1376 accept_connection(sock);
1377 #endif
1378
1379 now = time(NULL);
1380
1381 /*
1382 ** If there's a living child processing the page queue,
1383 ** we should just go back to waiting for more incoming
1384 ** network requests.
1385 */
1386 if (childpid > 0 && kill(childpid, 0) == 0) {
1387 #ifdef ENABLE_DEADMAN_TIMER
1388 /*
1389 ** Make sure the child hasn't wedged itself.
1390 ** It shouldn't take more than 5 minutes to
1391 ** do a queue run.
1392 */
1393 if (now - lastrun > 300) {
1394 qpage_log(LOG_WARNING, "killing stuck child");
1395 (void)kill(childpid, SIGKILL);
1396 }
1397 else
1398 #endif
1399 continue;
1400 }
1401
1402 /*
1403 ** hack to process new jobs without waiting for sleeptime
1404 */
1405 if (JobsPending) {
1406 JobsPending = FALSE;
1407 lastrun = 0;
1408 }
1409
1410 if (now - lastrun >= sleeptime && dontsend == FALSE) {
1411 lastrun = now;
1412
1413 qpage_log(LOG_DEBUG, "processing the page queue");
1414
1415 childpid = fork();
1416
1417 switch (childpid) {
1418 case (pid_t)-1:
1419 qpage_log(LOG_ERR,
1420 "fork() failed: %s",
1421 strerror(errno));
1422 break;
1423
1424 case 0:
1425 i = runqueue();
1426 _exit(i);
1427 }
1428 }
1429 }
1430 }
1431