xref: /original-bsd/usr.sbin/sendmail/src/daemon.c (revision 014fe330)
1 # include <errno.h>
2 # include "sendmail.h"
3 
4 #ifndef DAEMON
5 SCCSID(@(#)daemon.c	3.53		05/07/83	(w/o daemon mode));
6 #else
7 
8 #include <sys/socket.h>
9 #include <netinet/in.h>
10 #include <netdb.h>
11 #include <wait.h>
12 
13 SCCSID(@(#)daemon.c	3.53		05/07/83	(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 int	DaemonSocket = -1;		/* fd describing socket */
64 int	MaxConnections = 4;		/* maximum simultaneous sendmails */
65 
66 getrequests()
67 {
68 	int t;
69 	union wait status;
70 	int numconnections = 0;
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 		auto int lotherend;
132 		struct sockaddr_in otherend;
133 
134 		/* wait for a connection */
135 		register int pid;
136 
137 		do
138 		{
139 			errno = 0;
140 			lotherend = sizeof otherend;
141 			t = accept(DaemonSocket, &otherend, &lotherend, 0);
142 		} while (t < 0 && errno == EINTR);
143 		if (t < 0)
144 		{
145 			syserr("getrequests: accept");
146 			sleep(5);
147 			continue;
148 		}
149 
150 		/*
151 		**  Create a subprocess to process the mail.
152 		*/
153 
154 # ifdef DEBUG
155 		if (tTd(15, 2))
156 			printf("getrequests: forking (fd = %d)\n", t);
157 # endif DEBUG
158 
159 		pid = fork();
160 		if (pid < 0)
161 		{
162 			syserr("daemon: cannot fork");
163 			sleep(10);
164 			(void) close(t);
165 			continue;
166 		}
167 
168 		if (pid == 0)
169 		{
170 			extern struct hostent *gethostbyaddr();
171 			register struct hostent *hp;
172 			extern char *RealHostName;	/* srvrsmtp.c */
173 			char buf[MAXNAME];
174 
175 			/*
176 			**  CHILD -- return to caller.
177 			**	Collect verified idea of sending host.
178 			**	Verify calling user id if possible here.
179 			*/
180 
181 			/* determine host name */
182 			hp = gethostbyaddr(&otherend.sin_addr, sizeof otherend.sin_addr, AF_INET);
183 			if (hp != NULL)
184 				(void) sprintf(buf, "%s.ARPA", hp->h_name);
185 			else
186 				/* this should produce a dotted quad */
187 				(void) sprintf(buf, "%lx", otherend.sin_addr.s_addr);
188 			RealHostName = newstr(buf);
189 
190 			(void) close(DaemonSocket);
191 			InChannel = fdopen(t, "r");
192 			OutChannel = fdopen(t, "w");
193 # ifdef DEBUG
194 			if (tTd(15, 2))
195 				printf("getreq: returning\n");
196 # endif DEBUG
197 # ifdef LOG
198 			if (LogLevel > 11)
199 				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
200 # endif LOG
201 			return;
202 		}
203 
204 		/*
205 		**  PARENT -- wait for child to terminate.
206 		**	Perhaps we should allow concurrent processing?
207 		*/
208 
209 # ifdef DEBUG
210 		if (tTd(15, 2))
211 		{
212 			sleep(2);
213 			printf("getreq: parent waiting\n");
214 		}
215 # endif DEBUG
216 
217 		/* close the port so that others will hang (for a while) */
218 		(void) close(t);
219 
220 		/* pick up old zombies; implement load limiting */
221 		numconnections++;
222 		while (wait3(&status, numconnections < MaxConnections ? WNOHANG : 0, 0) > 0)
223 			numconnections--;
224 	}
225 	/*NOTREACHED*/
226 }
227 /*
228 **  CLRDAEMON -- reset the daemon connection
229 **
230 **	Parameters:
231 **		none.
232 **
233 **	Returns:
234 **		none.
235 **
236 **	Side Effects:
237 **		releases any resources used by the passive daemon.
238 */
239 
240 clrdaemon()
241 {
242 	if (DaemonSocket >= 0)
243 		(void) close(DaemonSocket);
244 	DaemonSocket = -1;
245 }
246 /*
247 **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
248 **
249 **	Parameters:
250 **		host -- the name of the host.
251 **		port -- the port number to connect to.
252 **		outfile -- a pointer to a place to put the outfile
253 **			descriptor.
254 **		infile -- ditto for infile.
255 **
256 **	Returns:
257 **		An exit code telling whether the connection could be
258 **			made and if not why not.
259 **
260 **	Side Effects:
261 **		none.
262 */
263 
264 makeconnection(host, port, outfile, infile)
265 	char *host;
266 	u_short port;
267 	FILE **outfile;
268 	FILE **infile;
269 {
270 	register int s;
271 
272 	/*
273 	**  Set up the address for the mailer.
274 	**	Accept "[a.b.c.d]" syntax for host name.
275 	*/
276 
277 	if (host[0] == '[')
278 	{
279 		long hid;
280 		register char *p = index(host, ']');
281 
282 		if (p != NULL)
283 		{
284 			*p = '\0';
285 			hid = inet_addr(&host[1]);
286 			*p = ']';
287 		}
288 		if (p == NULL || hid == -1)
289 		{
290 			usrerr("Invalid numeric domain spec \"%s\"", host);
291 			return (EX_NOHOST);
292 		}
293 		SendmailAddress.sin_addr.s_addr = hid;
294 	}
295 	else
296 	{
297 		register struct hostent *hp = gethostbyname(host);
298 
299 		if (hp == NULL)
300 			return (EX_NOHOST);
301 		bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
302 	}
303 
304 	/*
305 	**  Determine the port number.
306 	*/
307 
308 	if (port != 0)
309 		SendmailAddress.sin_port = htons(port);
310 	else
311 	{
312 		register struct servent *sp = getservbyname("smtp", "tcp");
313 
314 		if (sp == NULL)
315 		{
316 			syserr("makeconnection: server \"smtp\" unknown");
317 			return (EX_OSFILE);
318 		}
319 		SendmailAddress.sin_port = sp->s_port;
320 	}
321 
322 	/*
323 	**  Try to actually open the connection.
324 	*/
325 
326 # ifdef DEBUG
327 	if (tTd(16, 1))
328 		printf("makeconnection (%s)\n", host);
329 # endif DEBUG
330 
331 	s = socket(AF_INET, SOCK_STREAM, 0, 0);
332 	if (s < 0)
333 	{
334 		syserr("makeconnection: no socket");
335 		goto failure;
336 	}
337 
338 # ifdef DEBUG
339 	if (tTd(16, 1))
340 		printf("makeconnection: %d\n", s);
341 
342 	/* turn on network debugging? */
343 	if (tTd(16, 14))
344 		(void) setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0);
345 # endif DEBUG
346 	(void) fflush(CurEnv->e_xfp);			/* for debugging */
347 	SendmailAddress.sin_family = AF_INET;
348 	if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0)
349 	{
350 		/* failure, decide if temporary or not */
351 	failure:
352 		switch (errno)
353 		{
354 		  case EISCONN:
355 		  case ETIMEDOUT:
356 		  case EINPROGRESS:
357 		  case EALREADY:
358 		  case EADDRINUSE:
359 		  case EHOSTDOWN:
360 		  case ENETDOWN:
361 		  case ENETRESET:
362 		  case ENOBUFS:
363 		  case ECONNREFUSED:
364 		  case ECONNRESET:
365 		  case EHOSTUNREACH:
366 		  case ENETUNREACH:
367 			/* there are others, I'm sure..... */
368 			return (EX_TEMPFAIL);
369 
370 		  case EPERM:
371 			/* why is this happening? */
372 			syserr("makeconnection: funny failure, addr=%lx, port=%x",
373 				SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port);
374 			/* explicit fall-through */
375 
376 		  default:
377 			return (EX_UNAVAILABLE);
378 		}
379 	}
380 
381 	/* connection ok, put it into canonical form */
382 	*outfile = fdopen(s, "w");
383 	*infile = fdopen(s, "r");
384 
385 	return (EX_OK);
386 }
387 /*
388 **  MYHOSTNAME -- return the name of this host.
389 **
390 **	Parameters:
391 **		hostbuf -- a place to return the name of this host.
392 **		size -- the size of hostbuf.
393 **
394 **	Returns:
395 **		A list of aliases for this host.
396 **
397 **	Side Effects:
398 **		none.
399 */
400 
401 char **
402 myhostname(hostbuf, size)
403 	char hostbuf[];
404 	int size;
405 {
406 	extern struct hostent *gethostbyname();
407 	struct hostent *hp;
408 	auto int i = size;
409 	register char *p;
410 
411 	gethostname(hostbuf, &i);
412 	hp = gethostbyname(hostbuf);
413 	for (p = hostbuf; *p != '\0'; p++)
414 		if (islower(*p))
415 			*p -= 'a' - 'A';
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