1 # include <errno.h> 2 # include "sendmail.h" 3 4 #ifndef DAEMON 5 SCCSID(@(#)daemon.c 3.37 12/16/82 (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.37 12/16/82 (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 ** makeconnection(host, port, outfile, infile) 30 ** Make a connection to the named host on the given 31 ** port. Set *outfile and *infile to the files 32 ** appropriate for communication. Returns zero on 33 ** success, else an exit status describing the 34 ** error. 35 ** 36 ** The semantics of both of these should be clean. 37 */ 38 /* 39 ** GETREQUESTS -- open mail IPC port and get requests. 40 ** 41 ** Parameters: 42 ** none. 43 ** 44 ** Returns: 45 ** none. 46 ** 47 ** Side Effects: 48 ** Waits until some interesting activity occurs. When 49 ** it does, a child is created to process it, and the 50 ** parent waits for completion. Return from this 51 ** routine is always in the child. 52 */ 53 54 # define MAXCONNS 4 /* maximum simultaneous sendmails */ 55 56 struct sockaddr_in SendmailAddress; 57 58 getrequests() 59 { 60 int s; 61 int t; 62 union wait status; 63 int numconnections = 0; 64 struct sockaddr otherend; 65 register struct servent *sp; 66 67 /* 68 ** Set up the address for the mailer. 69 */ 70 71 sp = getservbyname("smtp", "tcp"); 72 if (sp == NULL) 73 { 74 syserr("server \"smtp\" unknown"); 75 return (-1); 76 } 77 SendmailAddress.sin_family = AF_INET; 78 SendmailAddress.sin_addr.s_addr = INADDR_ANY; 79 SendmailAddress.sin_port = sp->s_port; 80 81 /* 82 ** Try to actually open the connection. 83 */ 84 85 # ifdef DEBUG 86 if (tTd(15, 1)) 87 printf("getrequests: port 0x%x\n", SendmailAddress.sin_port); 88 # endif DEBUG 89 90 /* get a socket for the SMTP connection */ 91 s = socket(AF_INET, SOCK_STREAM, 0, 0); 92 if (s < 0) 93 { 94 /* probably another daemon already */ 95 syserr("getrequests: can't create socket"); 96 severe: 97 # ifdef LOG 98 if (LogLevel > 0) 99 syslog(LOG_SALERT, "cannot get connection"); 100 # endif LOG 101 finis(); 102 } 103 if (bind(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 104 { 105 syserr("getrequests: cannot bind"); 106 close(s); 107 goto severe; 108 } 109 listen(s, 10); 110 111 # ifdef DEBUG 112 if (tTd(15, 1)) 113 printf("getrequests: %d\n", s); 114 # endif DEBUG 115 116 for (;;) 117 { 118 /* wait for a connection */ 119 register int pid; 120 121 do 122 { 123 auto int lotherend; 124 struct sockaddr otherend; 125 126 errno = 0; 127 lotherend = sizeof otherend; 128 t = accept(s, &otherend, &lotherend, 0); 129 } while (t < 0 && errno == EINTR); 130 if (t < 0) 131 { 132 syserr("getrequests: accept"); 133 sleep(5); 134 continue; 135 } 136 137 /* 138 ** Create a subprocess to process the mail. 139 */ 140 141 # ifdef DEBUG 142 if (tTd(15, 2)) 143 printf("getrequests: forking (fd = %d)\n", t); 144 # endif DEBUG 145 146 pid = fork(); 147 if (pid < 0) 148 { 149 syserr("daemon: cannot fork"); 150 sleep(10); 151 (void) close(t); 152 continue; 153 } 154 155 if (pid == 0) 156 { 157 /* 158 ** CHILD -- return to caller. 159 ** Verify calling user id if possible here. 160 */ 161 162 close(s); 163 InChannel = fdopen(t, "r"); 164 OutChannel = fdopen(t, "w"); 165 # ifdef DEBUG 166 if (tTd(15, 2)) 167 printf("getreq: returning\n"); 168 # endif DEBUG 169 # ifdef LOG 170 if (LogLevel > 11) 171 syslog(LOG_DEBUG, "connected, pid=%d", getpid()); 172 # endif LOG 173 return; 174 } 175 176 /* 177 ** PARENT -- wait for child to terminate. 178 ** Perhaps we should allow concurrent processing? 179 */ 180 181 # ifdef DEBUG 182 if (tTd(15, 2)) 183 { 184 sleep(2); 185 printf("getreq: parent waiting\n"); 186 } 187 # endif DEBUG 188 189 /* close the port so that others will hang (for a while) */ 190 (void) close(t); 191 192 /* pick up old zombies; implement load limiting */ 193 numconnections++; 194 while (wait3(&status, numconnections < MAXCONNS ? WNOHANG : 0, 0) > 0) 195 numconnections--; 196 } 197 } 198 /* 199 ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 200 ** 201 ** Parameters: 202 ** host -- the name of the host. 203 ** port -- the port number to connect to. 204 ** outfile -- a pointer to a place to put the outfile 205 ** descriptor. 206 ** infile -- ditto for infile. 207 ** 208 ** Returns: 209 ** An exit code telling whether the connection could be 210 ** made and if not why not. 211 ** 212 ** Side Effects: 213 ** none. 214 */ 215 216 makeconnection(host, port, outfile, infile) 217 char *host; 218 u_short port; 219 FILE **outfile; 220 FILE **infile; 221 { 222 register int s; 223 224 /* 225 ** Set up the address for the mailer. 226 ** Accept "[a.b.c.d]" syntax for host name. 227 */ 228 229 if (host[0] == '[') 230 { 231 long hid = 0; 232 int i, j; 233 register char *p = host; 234 235 for (i = 3; i >= 0 && *p != ']' && *p != '\0'; i--) 236 { 237 j = 0; 238 while (isdigit(*++p)) 239 j = j * 10 + (*p - '0'); 240 if (*p != (i == 0 ? ']' : '.') || j > 255 || j < 0) 241 break; 242 hid |= j << ((3 - i) * 8); 243 } 244 if (i >= 0 || *p != ']' || *++p != '\0') 245 { 246 usrerr("Invalid numeric domain spec \"%s\"", host); 247 return (EX_NOHOST); 248 } 249 SendmailAddress.sin_addr.s_addr = hid; 250 } 251 else 252 { 253 register struct hostent *hp = gethostbyname(host); 254 255 if (hp == 0) 256 return (EX_NOHOST); 257 bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length); 258 } 259 260 /* 261 ** Determine the port number. 262 */ 263 264 if (port == 0) 265 { 266 register struct servent *sp = getservbyname("smtp", "tcp"); 267 268 if (sp == NULL) 269 { 270 syserr("makeconnection: server \"smtp\" unknown"); 271 return (EX_OSFILE); 272 } 273 port = sp->s_port; 274 } 275 SendmailAddress.sin_port = htons(port); 276 277 /* 278 ** Try to actually open the connection. 279 */ 280 281 # ifdef DEBUG 282 if (tTd(16, 1)) 283 printf("makeconnection (%s)\n", host); 284 # endif DEBUG 285 286 s = socket(AF_INET, SOCK_STREAM, 0, 0); 287 if (s < 0) 288 { 289 syserr("makeconnection: no socket"); 290 goto failure; 291 } 292 293 # ifdef DEBUG 294 if (tTd(16, 1)) 295 printf("makeconnection: %d\n", s); 296 # endif DEBUG 297 (void) fflush(CurEnv->e_xfp); /* for debugging */ 298 SendmailAddress.sin_family = AF_INET; 299 bind(s, &SendmailAddress, sizeof SendmailAddress, 0); 300 if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 301 { 302 /* failure, decide if temporary or not */ 303 failure: 304 switch (errno) 305 { 306 case EISCONN: 307 case ETIMEDOUT: 308 case EINPROGRESS: 309 case EALREADY: 310 case EADDRINUSE: 311 case ENETDOWN: 312 case ENETRESET: 313 case ENOBUFS: 314 case ECONNREFUSED: 315 /* there are others, I'm sure..... */ 316 return (EX_TEMPFAIL); 317 318 default: 319 return (EX_UNAVAILABLE); 320 } 321 } 322 323 /* connection ok, put it into canonical form */ 324 *outfile = fdopen(s, "w"); 325 *infile = fdopen(s, "r"); 326 327 return (0); 328 } 329 330 #endif DAEMON 331