xref: /original-bsd/usr.sbin/sendmail/src/daemon.c (revision cfde0222)
1 # include <errno.h>
2 # include "sendmail.h"
3 
4 #ifndef DAEMON
5 SCCSID(@(#)daemon.c	4.12		05/24/85	(w/o daemon mode));
6 #else
7 
8 #include <sys/socket.h>
9 #include <netinet/in.h>
10 #include <netdb.h>
11 #include <sys/wait.h>
12 
13 SCCSID(@(#)daemon.c	4.12		05/24/85	(with daemon mode));
14 
15 /*
16 **  DAEMON.C -- routines to use when running as a daemon.
17 **
18 **	This entire file is highly dependent on the 4.2 BSD
19 **	interprocess communication primitives.  No attempt has
20 **	been made to make this file portable to Version 7,
21 **	Version 6, MPX files, etc.  If you should try such a
22 **	thing yourself, I recommend chucking the entire file
23 **	and starting from scratch.  Basic semantics are:
24 **
25 **	getrequests()
26 **		Opens a port and initiates a connection.
27 **		Returns in a child.  Must set InChannel and
28 **		OutChannel appropriately.
29 **	clrdaemon()
30 **		Close any open files associated with getting
31 **		the connection; this is used when running the queue,
32 **		etc., to avoid having extra file descriptors during
33 **		the queue run and to avoid confusing the network
34 **		code (if it cares).
35 **	makeconnection(host, port, outfile, infile)
36 **		Make a connection to the named host on the given
37 **		port.  Set *outfile and *infile to the files
38 **		appropriate for communication.  Returns zero on
39 **		success, else an exit status describing the
40 **		error.
41 **
42 **	The semantics of both of these should be clean.
43 */
44 /*
45 **  GETREQUESTS -- open mail IPC port and get requests.
46 **
47 **	Parameters:
48 **		none.
49 **
50 **	Returns:
51 **		none.
52 **
53 **	Side Effects:
54 **		Waits until some interesting activity occurs.  When
55 **		it does, a child is created to process it, and the
56 **		parent waits for completion.  Return from this
57 **		routine is always in the child.  The file pointers
58 **		"InChannel" and "OutChannel" should be set to point
59 **		to the communication channel.
60 */
61 
62 struct sockaddr_in	SendmailAddress;/* internet address of sendmail */
63 
64 int	DaemonSocket	= -1;		/* fd describing socket */
65 char	*NetName;			/* name of home (local?) network */
66 
67 getrequests()
68 {
69 	int t;
70 	union wait status;
71 	register struct servent *sp;
72 
73 	/*
74 	**  Set up the address for the mailer.
75 	*/
76 
77 	sp = getservbyname("smtp", "tcp");
78 	if (sp == NULL)
79 	{
80 		syserr("server \"smtp\" unknown");
81 		goto severe;
82 	}
83 	SendmailAddress.sin_family = AF_INET;
84 	SendmailAddress.sin_addr.s_addr = INADDR_ANY;
85 	SendmailAddress.sin_port = sp->s_port;
86 
87 	/*
88 	**  Try to actually open the connection.
89 	*/
90 
91 # ifdef DEBUG
92 	if (tTd(15, 1))
93 		printf("getrequests: port 0x%x\n", SendmailAddress.sin_port);
94 # endif DEBUG
95 
96 	/* get a socket for the SMTP connection */
97 	DaemonSocket = socket(AF_INET, SOCK_STREAM, 0, 0);
98 	if (DaemonSocket < 0)
99 	{
100 		/* probably another daemon already */
101 		syserr("getrequests: can't create socket");
102 	  severe:
103 # ifdef LOG
104 		if (LogLevel > 0)
105 			syslog(LOG_SALERT, "cannot get connection");
106 # endif LOG
107 		finis();
108 	}
109 
110 #ifdef DEBUG
111 	/* turn on network debugging? */
112 	if (tTd(15, 15))
113 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, 0, 0);
114 #endif DEBUG
115 
116 	if (bind(DaemonSocket, &SendmailAddress, sizeof SendmailAddress, 0) < 0)
117 	{
118 		syserr("getrequests: cannot bind");
119 		(void) close(DaemonSocket);
120 		goto severe;
121 	}
122 	listen(DaemonSocket, 10);
123 
124 # ifdef DEBUG
125 	if (tTd(15, 1))
126 		printf("getrequests: %d\n", DaemonSocket);
127 # endif DEBUG
128 
129 	for (;;)
130 	{
131 		register int pid;
132 		auto int lotherend;
133 		struct sockaddr_in otherend;
134 		extern int RefuseLA;
135 
136 		/* see if we are rejecting connections */
137 		while (getla() > RefuseLA)
138 			sleep(5);
139 
140 		/* wait for a connection */
141 		do
142 		{
143 			errno = 0;
144 			lotherend = sizeof otherend;
145 			t = accept(DaemonSocket, &otherend, &lotherend, 0);
146 		} while (t < 0 && errno == EINTR);
147 		if (t < 0)
148 		{
149 			syserr("getrequests: accept");
150 			sleep(5);
151 			continue;
152 		}
153 
154 		/*
155 		**  Create a subprocess to process the mail.
156 		*/
157 
158 # ifdef DEBUG
159 		if (tTd(15, 2))
160 			printf("getrequests: forking (fd = %d)\n", t);
161 # endif DEBUG
162 
163 		pid = fork();
164 		if (pid < 0)
165 		{
166 			syserr("daemon: cannot fork");
167 			sleep(10);
168 			(void) close(t);
169 			continue;
170 		}
171 
172 		if (pid == 0)
173 		{
174 			extern struct hostent *gethostbyaddr();
175 			register struct hostent *hp;
176 			extern char *RealHostName;	/* srvrsmtp.c */
177 			char buf[MAXNAME];
178 
179 			/*
180 			**  CHILD -- return to caller.
181 			**	Collect verified idea of sending host.
182 			**	Verify calling user id if possible here.
183 			*/
184 
185 			/* determine host name */
186 			hp = gethostbyaddr(&otherend.sin_addr, sizeof otherend.sin_addr, AF_INET);
187 			if (hp != NULL)
188 			{
189 				strcpy(buf, hp->h_name);
190 				if (NetName != NULL && NetName[0] != '\0' &&
191 				    index(hp->h_name, '.') == NULL)
192 				{
193 					strcat(buf, ".");
194 					strcat(buf, NetName);
195 				}
196 			}
197 			else
198 			{
199 				extern char *inet_ntoa();
200 
201 				/* produce a dotted quad */
202 				(void) sprintf(buf, "[%s]",
203 					inet_ntoa(otherend.sin_addr));
204 			}
205 
206 			/* should we check for illegal connection here? XXX */
207 
208 			RealHostName = newstr(buf);
209 
210 			(void) close(DaemonSocket);
211 			InChannel = fdopen(t, "r");
212 			OutChannel = fdopen(dup(t), "w");
213 # ifdef DEBUG
214 			if (tTd(15, 2))
215 				printf("getreq: returning\n");
216 # endif DEBUG
217 # ifdef LOG
218 			if (LogLevel > 11)
219 				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
220 # endif LOG
221 			return;
222 		}
223 
224 		/*
225 		**  PARENT -- wait for child to terminate.
226 		**	Perhaps we should allow concurrent processing?
227 		*/
228 
229 # ifdef DEBUG
230 		if (tTd(15, 2))
231 		{
232 			sleep(2);
233 			printf("getreq: parent waiting\n");
234 		}
235 # endif DEBUG
236 
237 		/* close the port so that others will hang (for a while) */
238 		(void) close(t);
239 
240 		/* pick up old zombies */
241 		while (wait3(&status, WNOHANG, 0) > 0)
242 			continue;
243 	}
244 	/*NOTREACHED*/
245 }
246 /*
247 **  CLRDAEMON -- reset the daemon connection
248 **
249 **	Parameters:
250 **		none.
251 **
252 **	Returns:
253 **		none.
254 **
255 **	Side Effects:
256 **		releases any resources used by the passive daemon.
257 */
258 
259 clrdaemon()
260 {
261 	if (DaemonSocket >= 0)
262 		(void) close(DaemonSocket);
263 	DaemonSocket = -1;
264 }
265 /*
266 **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
267 **
268 **	Parameters:
269 **		host -- the name of the host.
270 **		port -- the port number to connect to.
271 **		outfile -- a pointer to a place to put the outfile
272 **			descriptor.
273 **		infile -- ditto for infile.
274 **
275 **	Returns:
276 **		An exit code telling whether the connection could be
277 **			made and if not why not.
278 **
279 **	Side Effects:
280 **		none.
281 */
282 
283 makeconnection(host, port, outfile, infile)
284 	char *host;
285 	u_short port;
286 	FILE **outfile;
287 	FILE **infile;
288 {
289 	register int s;
290 
291 	/*
292 	**  Set up the address for the mailer.
293 	**	Accept "[a.b.c.d]" syntax for host name.
294 	*/
295 
296 	if (host[0] == '[')
297 	{
298 		long hid;
299 		register char *p = index(host, ']');
300 
301 		if (p != NULL)
302 		{
303 			*p = '\0';
304 			hid = inet_addr(&host[1]);
305 			*p = ']';
306 		}
307 		if (p == NULL || hid == -1)
308 		{
309 			usrerr("Invalid numeric domain spec \"%s\"", host);
310 			return (EX_NOHOST);
311 		}
312 		SendmailAddress.sin_addr.s_addr = hid;
313 	}
314 	else
315 	{
316 		register struct hostent *hp = gethostbyname(host);
317 
318 		if (hp == NULL)
319 			return (EX_NOHOST);
320 		bcopy(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
321 	}
322 
323 	/*
324 	**  Determine the port number.
325 	*/
326 
327 	if (port != 0)
328 		SendmailAddress.sin_port = htons(port);
329 	else
330 	{
331 		register struct servent *sp = getservbyname("smtp", "tcp");
332 
333 		if (sp == NULL)
334 		{
335 			syserr("makeconnection: server \"smtp\" unknown");
336 			return (EX_OSFILE);
337 		}
338 		SendmailAddress.sin_port = sp->s_port;
339 	}
340 
341 	/*
342 	**  Try to actually open the connection.
343 	*/
344 
345 # ifdef DEBUG
346 	if (tTd(16, 1))
347 		printf("makeconnection (%s)\n", host);
348 # endif DEBUG
349 
350 	s = socket(AF_INET, SOCK_STREAM, 0, 0);
351 	if (s < 0)
352 	{
353 		syserr("makeconnection: no socket");
354 		goto failure;
355 	}
356 
357 # ifdef DEBUG
358 	if (tTd(16, 1))
359 		printf("makeconnection: %d\n", s);
360 
361 	/* turn on network debugging? */
362 	if (tTd(16, 14))
363 		(void) setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0);
364 # endif DEBUG
365 	(void) fflush(CurEnv->e_xfp);			/* for debugging */
366 	errno = 0;					/* for debugging */
367 	SendmailAddress.sin_family = AF_INET;
368 	if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0)
369 	{
370 		/* failure, decide if temporary or not */
371 	failure:
372 		switch (errno)
373 		{
374 		  case EISCONN:
375 		  case ETIMEDOUT:
376 		  case EINPROGRESS:
377 		  case EALREADY:
378 		  case EADDRINUSE:
379 		  case EHOSTDOWN:
380 		  case ENETDOWN:
381 		  case ENETRESET:
382 		  case ENOBUFS:
383 		  case ECONNREFUSED:
384 		  case ECONNRESET:
385 		  case EHOSTUNREACH:
386 		  case ENETUNREACH:
387 			/* there are others, I'm sure..... */
388 			CurEnv->e_flags &= ~EF_FATALERRS;
389 			return (EX_TEMPFAIL);
390 
391 		  case EPERM:
392 			/* why is this happening? */
393 			syserr("makeconnection: funny failure, addr=%lx, port=%x",
394 				SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port);
395 			return (EX_TEMPFAIL);
396 
397 		  default:
398 			return (EX_UNAVAILABLE);
399 		}
400 	}
401 
402 	/* connection ok, put it into canonical form */
403 	*outfile = fdopen(s, "w");
404 	*infile = fdopen(s, "r");
405 
406 	return (EX_OK);
407 }
408 /*
409 **  MYHOSTNAME -- return the name of this host.
410 **
411 **	Parameters:
412 **		hostbuf -- a place to return the name of this host.
413 **		size -- the size of hostbuf.
414 **
415 **	Returns:
416 **		A list of aliases for this host.
417 **
418 **	Side Effects:
419 **		none.
420 */
421 
422 char **
423 myhostname(hostbuf, size)
424 	char hostbuf[];
425 	int size;
426 {
427 	extern struct hostent *gethostbyname();
428 	struct hostent *hp;
429 
430 	gethostname(hostbuf, size);
431 	hp = gethostbyname(hostbuf);
432 	if (hp != NULL)
433 	{
434 		strcpy(hostbuf, hp->h_name);
435 		return (hp->h_aliases);
436 	}
437 	else
438 		return (NULL);
439 }
440 /*
441 **  MAPHOSTNAME -- turn a hostname into canonical form
442 **
443 **	Parameters:
444 **		hbuf -- a buffer containing a hostname.
445 **		hbsize -- the size of hbuf.
446 **
447 **	Returns:
448 **		none.
449 **
450 **	Side Effects:
451 **		Looks up the host specified in hbuf.  If it is not
452 **		the canonical name for that host, replace it with
453 **		the canonical name.  If the name is unknown, or it
454 **		is already the canonical name, leave it unchanged.
455 */
456 
457 maphostname(hbuf, hbsize)
458 	char *hbuf;
459 	int hbsize;
460 {
461 	register struct hostent *hp;
462 	extern struct hostent *gethostbyname();
463 
464 	makelower(hbuf);
465 	hp = gethostbyname(hbuf);
466 	if (hp != NULL)
467 	{
468 		int i = strlen(hp->h_name);
469 
470 		if (i >= hbsize)
471 			hp->h_name[--i] = '\0';
472 		strcpy(hbuf, hp->h_name);
473 	}
474 }
475 
476 # else DAEMON
477 /* code for systems without sophisticated networking */
478 
479 /*
480 **  MYHOSTNAME -- stub version for case of no daemon code.
481 **
482 **	Can't convert to upper case here because might be a UUCP name.
483 **
484 **	Mark, you can change this to be anything you want......
485 */
486 
487 char **
488 myhostname(hostbuf, size)
489 	char hostbuf[];
490 	int size;
491 {
492 	register FILE *f;
493 
494 	hostbuf[0] = '\0';
495 	f = fopen("/usr/include/whoami", "r");
496 	if (f != NULL)
497 	{
498 		(void) fgets(hostbuf, size, f);
499 		fixcrlf(hostbuf, TRUE);
500 		(void) fclose(f);
501 	}
502 	return (NULL);
503 }
504 /*
505 **  MAPHOSTNAME -- turn a hostname into canonical form
506 **
507 **	Parameters:
508 **		hbuf -- a buffer containing a hostname.
509 **		hbsize -- the size of hbuf.
510 **
511 **	Returns:
512 **		none.
513 **
514 **	Side Effects:
515 **		Looks up the host specified in hbuf.  If it is not
516 **		the canonical name for that host, replace it with
517 **		the canonical name.  If the name is unknown, or it
518 **		is already the canonical name, leave it unchanged.
519 */
520 
521 /*ARGSUSED*/
522 maphostname(hbuf, hbsize)
523 	char *hbuf;
524 	int hbsize;
525 {
526 	return;
527 }
528 
529 
530 #endif DAEMON
531