1 /* $OpenBSD: queue_backend.c,v 1.38 2012/09/01 16:09:14 gilles Exp $ */ 2 3 /* 4 * Copyright (c) 2011 Gilles Chehade <gilles@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/tree.h> 22 #include <sys/param.h> 23 #include <sys/socket.h> 24 #include <sys/stat.h> 25 26 #include <ctype.h> 27 #include <errno.h> 28 #include <event.h> 29 #include <fcntl.h> 30 #include <imsg.h> 31 #include <inttypes.h> 32 #include <libgen.h> 33 #include <pwd.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <time.h> 38 #include <unistd.h> 39 40 #include "smtpd.h" 41 #include "log.h" 42 43 static const char* envelope_validate(struct envelope *); 44 45 extern struct queue_backend queue_backend_fs; 46 47 int 48 queue_message_incoming_path(uint32_t msgid, char *buf, size_t len) 49 { 50 return bsnprintf(buf, len, "%s/%08x", 51 PATH_INCOMING, 52 msgid); 53 } 54 55 int 56 queue_envelope_incoming_path(uint64_t evpid, char *buf, size_t len) 57 { 58 return bsnprintf(buf, len, "%s/%08x%s/%016" PRIx64, 59 PATH_INCOMING, 60 evpid_to_msgid(evpid), 61 PATH_ENVELOPES, 62 evpid); 63 } 64 65 int 66 queue_message_incoming_delete(uint32_t msgid) 67 { 68 char rootdir[MAXPATHLEN]; 69 70 if (! queue_message_incoming_path(msgid, rootdir, sizeof(rootdir))) 71 fatal("queue_message_incoming_delete: snprintf"); 72 73 if (rmtree(rootdir, 0) == -1) 74 fatal("queue_message_incoming_delete: rmtree"); 75 76 return 1; 77 } 78 79 struct queue_backend * 80 queue_backend_lookup(const char *name) 81 { 82 if (!strcmp(name, "fs")) 83 return &queue_backend_fs; 84 85 return (NULL); 86 } 87 88 int 89 queue_message_create(uint32_t *msgid) 90 { 91 return env->sc_queue->message(QOP_CREATE, msgid); 92 } 93 94 int 95 queue_message_delete(uint32_t msgid) 96 { 97 return env->sc_queue->message(QOP_DELETE, &msgid); 98 } 99 100 int 101 queue_message_commit(uint32_t msgid) 102 { 103 char msgpath[MAXPATHLEN]; 104 char tmppath[MAXPATHLEN]; 105 FILE *ifp = NULL; 106 FILE *ofp = NULL; 107 108 queue_message_incoming_path(msgid, msgpath, sizeof msgpath); 109 strlcat(msgpath, PATH_MESSAGE, sizeof(msgpath)); 110 111 if (env->sc_queue_flags & QUEUE_COMPRESS) { 112 113 bsnprintf(tmppath, sizeof tmppath, "%s.comp", msgpath); 114 ifp = fopen(msgpath, "r"); 115 ofp = fopen(tmppath, "w+"); 116 if (ifp == NULL || ofp == NULL) 117 goto err; 118 if (! compress_file(ifp, ofp)) 119 goto err; 120 fclose(ifp); 121 fclose(ofp); 122 ifp = NULL; 123 ofp = NULL; 124 125 if (rename(tmppath, msgpath) == -1) { 126 if (errno == ENOSPC) 127 return (0); 128 fatal("queue_message_commit: rename"); 129 } 130 } 131 132 return env->sc_queue->message(QOP_COMMIT, &msgid); 133 134 err: 135 if (ifp) 136 fclose(ifp); 137 if (ofp) 138 fclose(ofp); 139 return 0; 140 } 141 142 int 143 queue_message_corrupt(uint32_t msgid) 144 { 145 return env->sc_queue->message(QOP_CORRUPT, &msgid); 146 } 147 148 int 149 queue_message_fd_r(uint32_t msgid) 150 { 151 int fdin = -1, fdout = -1; 152 FILE *ifp = NULL; 153 FILE *ofp = NULL; 154 155 fdin = env->sc_queue->message(QOP_FD_R, &msgid); 156 157 if (env->sc_queue_flags & QUEUE_COMPRESS) { 158 fdout = mktmpfile(); 159 ifp = fdopen(fdin, "r"); 160 ofp = fdopen(fdout, "w+"); 161 if (ifp == NULL || ofp == NULL) 162 goto err; 163 if (! uncompress_file(ifp, ofp)) 164 goto err; 165 fseek(ofp, SEEK_SET, 0); 166 fdin = fileno(ofp); 167 fclose(ifp); 168 ifp = NULL; 169 ofp = NULL; 170 } 171 172 return (fdin); 173 174 err: 175 if (fdin != -1) 176 close(fdin); 177 if (fdout != -1) 178 close(fdout); 179 if (ifp) 180 fclose(ifp); 181 if (ofp) 182 fclose(ofp); 183 return -1; 184 } 185 186 int 187 queue_message_fd_rw(uint32_t msgid) 188 { 189 char msgpath[MAXPATHLEN]; 190 191 queue_message_incoming_path(msgid, msgpath, sizeof msgpath); 192 strlcat(msgpath, PATH_MESSAGE, sizeof(msgpath)); 193 194 return open(msgpath, O_RDWR | O_CREAT | O_EXCL, 0600); 195 } 196 197 static int 198 queue_envelope_dump_buffer(struct envelope *ep, char *evpbuf, size_t evpbufsize) 199 { 200 char evpbufcom[sizeof(struct envelope)]; 201 char *evp; 202 size_t evplen; 203 204 evp = evpbuf; 205 evplen = envelope_dump_buffer(ep, evpbuf, evpbufsize); 206 if (evplen == 0) 207 return (0); 208 209 if (env->sc_queue_flags & QUEUE_COMPRESS) { 210 evplen = compress_buffer(evp, evplen, evpbufcom, sizeof evpbufcom); 211 if (evplen == 0) 212 return (0); 213 evp = evpbufcom; 214 } 215 216 memmove(evpbuf, evp, evplen); 217 218 return (evplen); 219 } 220 221 static int 222 queue_envelope_load_buffer(struct envelope *ep, char *evpbuf, size_t evpbufsize) 223 { 224 char evpbufcom[sizeof(struct envelope)]; 225 char *evp; 226 size_t evplen; 227 228 evp = evpbuf; 229 evplen = evpbufsize; 230 231 if (env->sc_queue_flags & QUEUE_COMPRESS) { 232 evplen = uncompress_buffer(evp, evplen, evpbufcom, sizeof evpbufcom); 233 if (evplen == 0) 234 return (0); 235 evp = evpbufcom; 236 } 237 238 return (envelope_load_buffer(ep, evp, evplen)); 239 } 240 241 int 242 queue_envelope_create(struct envelope *ep) 243 { 244 int r; 245 char evpbuf[sizeof(struct envelope)]; 246 size_t evplen; 247 248 ep->creation = time(NULL); 249 evplen = queue_envelope_dump_buffer(ep, evpbuf, sizeof evpbuf); 250 if (evplen == 0) 251 return (0); 252 253 r = env->sc_queue->envelope(QOP_CREATE, &ep->id, evpbuf, evplen); 254 if (!r) { 255 ep->creation = 0; 256 ep->id = 0; 257 } 258 return (r); 259 } 260 261 int 262 queue_envelope_delete(struct envelope *ep) 263 { 264 return env->sc_queue->envelope(QOP_DELETE, &ep->id, NULL, 0); 265 } 266 267 int 268 queue_envelope_load(uint64_t evpid, struct envelope *ep) 269 { 270 const char *e; 271 char evpbuf[sizeof(struct envelope)]; 272 size_t evplen; 273 274 ep->id = evpid; 275 evplen = env->sc_queue->envelope(QOP_LOAD, &ep->id, evpbuf, sizeof evpbuf); 276 if (evplen == 0) 277 return (0); 278 279 if (queue_envelope_load_buffer(ep, evpbuf, evplen)) { 280 if ((e = envelope_validate(ep)) == NULL) { 281 ep->id = evpid; 282 return (1); 283 } 284 log_debug("invalid envelope %016" PRIx64 ": %s", ep->id, e); 285 } 286 return (0); 287 } 288 289 int 290 queue_envelope_update(struct envelope *ep) 291 { 292 char evpbuf[sizeof(struct envelope)]; 293 size_t evplen; 294 295 evplen = queue_envelope_dump_buffer(ep, evpbuf, sizeof evpbuf); 296 if (evplen == 0) 297 return (0); 298 299 return env->sc_queue->envelope(QOP_UPDATE, &ep->id, evpbuf, evplen); 300 } 301 302 void * 303 qwalk_new(uint32_t msgid) 304 { 305 return env->sc_queue->qwalk_new(msgid); 306 } 307 308 int 309 qwalk(void *hdl, uint64_t *evpid) 310 { 311 return env->sc_queue->qwalk(hdl, evpid); 312 } 313 314 void 315 qwalk_close(void *hdl) 316 { 317 return env->sc_queue->qwalk_close(hdl); 318 } 319 320 uint32_t 321 queue_generate_msgid(void) 322 { 323 uint32_t msgid; 324 325 while((msgid = arc4random_uniform(0xffffffff)) == 0) 326 ; 327 328 return msgid; 329 } 330 331 uint64_t 332 queue_generate_evpid(uint32_t msgid) 333 { 334 uint32_t rnd; 335 uint64_t evpid; 336 337 while((rnd = arc4random_uniform(0xffffffff)) == 0) 338 ; 339 340 evpid = msgid; 341 evpid <<= 32; 342 evpid |= rnd; 343 344 return evpid; 345 } 346 347 348 /**/ 349 static const char* 350 envelope_validate(struct envelope *ep) 351 { 352 if (ep->version != SMTPD_ENVELOPE_VERSION) 353 return "version mismatch"; 354 355 if (memchr(ep->helo, '\0', sizeof(ep->helo)) == NULL) 356 return "invalid helo"; 357 if (ep->helo[0] == '\0') 358 return "empty helo"; 359 360 if (memchr(ep->hostname, '\0', sizeof(ep->hostname)) == NULL) 361 return "invalid hostname"; 362 if (ep->hostname[0] == '\0') 363 return "empty hostname"; 364 365 if (memchr(ep->errorline, '\0', sizeof(ep->errorline)) == NULL) 366 return "invalid error line"; 367 368 return NULL; 369 } 370