xref: /csrg-svn/usr.sbin/sendmail/src/daemon.c (revision 16144)
1 # include <errno.h>
2 # include "sendmail.h"
3 
4 #ifndef DAEMON
5 SCCSID(@(#)daemon.c	4.5		03/11/84	(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.5		03/11/84	(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	= "ARPA";	/* 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 				(void) sprintf(buf, "%s.%s", hp->h_name, NetName);
189 			else
190 				/* this should produce a dotted quad */
191 				(void) sprintf(buf, "%lx", otherend.sin_addr.s_addr);
192 			RealHostName = newstr(buf);
193 
194 			(void) close(DaemonSocket);
195 			InChannel = fdopen(t, "r");
196 			OutChannel = fdopen(t, "w");
197 # ifdef DEBUG
198 			if (tTd(15, 2))
199 				printf("getreq: returning\n");
200 # endif DEBUG
201 # ifdef LOG
202 			if (LogLevel > 11)
203 				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
204 # endif LOG
205 			return;
206 		}
207 
208 		/*
209 		**  PARENT -- wait for child to terminate.
210 		**	Perhaps we should allow concurrent processing?
211 		*/
212 
213 # ifdef DEBUG
214 		if (tTd(15, 2))
215 		{
216 			sleep(2);
217 			printf("getreq: parent waiting\n");
218 		}
219 # endif DEBUG
220 
221 		/* close the port so that others will hang (for a while) */
222 		(void) close(t);
223 
224 		/* pick up old zombies */
225 		while (wait3(&status, WNOHANG, 0) > 0)
226 			continue;
227 	}
228 	/*NOTREACHED*/
229 }
230 /*
231 **  CLRDAEMON -- reset the daemon connection
232 **
233 **	Parameters:
234 **		none.
235 **
236 **	Returns:
237 **		none.
238 **
239 **	Side Effects:
240 **		releases any resources used by the passive daemon.
241 */
242 
243 clrdaemon()
244 {
245 	if (DaemonSocket >= 0)
246 		(void) close(DaemonSocket);
247 	DaemonSocket = -1;
248 }
249 /*
250 **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
251 **
252 **	Parameters:
253 **		host -- the name of the host.
254 **		port -- the port number to connect to.
255 **		outfile -- a pointer to a place to put the outfile
256 **			descriptor.
257 **		infile -- ditto for infile.
258 **
259 **	Returns:
260 **		An exit code telling whether the connection could be
261 **			made and if not why not.
262 **
263 **	Side Effects:
264 **		none.
265 */
266 
267 makeconnection(host, port, outfile, infile)
268 	char *host;
269 	u_short port;
270 	FILE **outfile;
271 	FILE **infile;
272 {
273 	register int s;
274 
275 	/*
276 	**  Set up the address for the mailer.
277 	**	Accept "[a.b.c.d]" syntax for host name.
278 	*/
279 
280 	if (host[0] == '[')
281 	{
282 		long hid;
283 		register char *p = index(host, ']');
284 
285 		if (p != NULL)
286 		{
287 			*p = '\0';
288 			hid = inet_addr(&host[1]);
289 			*p = ']';
290 		}
291 		if (p == NULL || hid == -1)
292 		{
293 			usrerr("Invalid numeric domain spec \"%s\"", host);
294 			return (EX_NOHOST);
295 		}
296 		SendmailAddress.sin_addr.s_addr = hid;
297 	}
298 	else
299 	{
300 		register struct hostent *hp = gethostbyname(host);
301 
302 		if (hp == NULL)
303 			return (EX_NOHOST);
304 		bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
305 	}
306 
307 	/*
308 	**  Determine the port number.
309 	*/
310 
311 	if (port != 0)
312 		SendmailAddress.sin_port = htons(port);
313 	else
314 	{
315 		register struct servent *sp = getservbyname("smtp", "tcp");
316 
317 		if (sp == NULL)
318 		{
319 			syserr("makeconnection: server \"smtp\" unknown");
320 			return (EX_OSFILE);
321 		}
322 		SendmailAddress.sin_port = sp->s_port;
323 	}
324 
325 	/*
326 	**  Try to actually open the connection.
327 	*/
328 
329 # ifdef DEBUG
330 	if (tTd(16, 1))
331 		printf("makeconnection (%s)\n", host);
332 # endif DEBUG
333 
334 	s = socket(AF_INET, SOCK_STREAM, 0, 0);
335 	if (s < 0)
336 	{
337 		syserr("makeconnection: no socket");
338 		goto failure;
339 	}
340 
341 # ifdef DEBUG
342 	if (tTd(16, 1))
343 		printf("makeconnection: %d\n", s);
344 
345 	/* turn on network debugging? */
346 	if (tTd(16, 14))
347 		(void) setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0);
348 # endif DEBUG
349 	(void) fflush(CurEnv->e_xfp);			/* for debugging */
350 	errno = 0;					/* for debugging */
351 	SendmailAddress.sin_family = AF_INET;
352 	if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0)
353 	{
354 		/* failure, decide if temporary or not */
355 	failure:
356 		switch (errno)
357 		{
358 		  case EISCONN:
359 		  case ETIMEDOUT:
360 		  case EINPROGRESS:
361 		  case EALREADY:
362 		  case EADDRINUSE:
363 		  case EHOSTDOWN:
364 		  case ENETDOWN:
365 		  case ENETRESET:
366 		  case ENOBUFS:
367 		  case ECONNREFUSED:
368 		  case ECONNRESET:
369 		  case EHOSTUNREACH:
370 		  case ENETUNREACH:
371 			/* there are others, I'm sure..... */
372 			return (EX_TEMPFAIL);
373 
374 		  case EPERM:
375 			/* why is this happening? */
376 			syserr("makeconnection: funny failure, addr=%lx, port=%x",
377 				SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port);
378 			return (EX_TEMPFAIL);
379 
380 		  default:
381 			return (EX_UNAVAILABLE);
382 		}
383 	}
384 
385 	/* connection ok, put it into canonical form */
386 	*outfile = fdopen(s, "w");
387 	*infile = fdopen(s, "r");
388 
389 	return (EX_OK);
390 }
391 /*
392 **  MYHOSTNAME -- return the name of this host.
393 **
394 **	Parameters:
395 **		hostbuf -- a place to return the name of this host.
396 **		size -- the size of hostbuf.
397 **
398 **	Returns:
399 **		A list of aliases for this host.
400 **
401 **	Side Effects:
402 **		none.
403 */
404 
405 char **
406 myhostname(hostbuf, size)
407 	char hostbuf[];
408 	int size;
409 {
410 	extern struct hostent *gethostbyname();
411 	struct hostent *hp;
412 	auto int i = size;
413 
414 	gethostname(hostbuf, &i);
415 	hp = gethostbyname(hostbuf);
416 	if (hp != NULL)
417 		return (hp->h_aliases);
418 	else
419 		return (NULL);
420 }
421 
422 # else DAEMON
423 
424 /*
425 **  MYHOSTNAME -- stub version for case of no daemon code.
426 **
427 **	Can't convert to upper case here because might be a UUCP name.
428 **
429 **	Mark, you can change this to be anything you want......
430 */
431 
432 char **
433 myhostname(hostbuf, size)
434 	char hostbuf[];
435 	int size;
436 {
437 	register FILE *f;
438 
439 	hostbuf[0] = '\0';
440 	f = fopen("/usr/include/whoami", "r");
441 	if (f != NULL)
442 	{
443 		(void) fgets(hostbuf, size, f);
444 		fixcrlf(hostbuf, TRUE);
445 		(void) fclose(f);
446 	}
447 	return (NULL);
448 }
449 
450 #endif DAEMON
451