1 /* Copyright (c) 2013, Vsevolod Stakhov
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 *
12 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
13 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
16 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
19 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 */
23
24 #include "config.h"
25 #include "util.h"
26 #include "rmilter.h"
27 #include <assert.h>
28 #include <stdbool.h>
29
30
31 extern const char *_rmilter_progname;
32
33 size_t
rmilter_strlcpy(char * dst,const char * src,size_t siz)34 rmilter_strlcpy(char *dst, const char *src, size_t siz)
35 {
36 char *d = dst;
37 const char *s = src;
38 size_t n = siz;
39
40 /* Copy as many bytes as will fit */
41 if (n != 0) {
42 while (--n != 0) {
43 if ((*d++ = *s++) == '\0') {
44 break;
45 }
46 }
47 }
48
49 if (n == 0 && siz != 0) {
50 *d = '\0';
51 }
52
53 return (s - src - 1); /* count does not include NUL */
54 }
55
56 bool
rmilter_file_lock(int fd,bool async)57 rmilter_file_lock(int fd, bool async)
58 {
59 int flags;
60
61 if (async) {
62 flags = LOCK_EX | LOCK_NB;
63 }
64 else {
65 flags = LOCK_EX;
66 }
67
68 if (flock (fd, flags) == -1) {
69 if (async && errno == EAGAIN) {
70 return false;
71 }
72
73 return false;
74 }
75
76 return true;
77 }
78
79 bool
rmilter_file_unlock(int fd,bool async)80 rmilter_file_unlock(int fd, bool async)
81 {
82 int flags;
83
84 if (async) {
85 flags = LOCK_UN | LOCK_NB;
86 }
87 else {
88 flags = LOCK_UN;
89 }
90
91 if (flock (fd, flags) == -1) {
92 if (async && errno == EAGAIN) {
93 return false;
94 }
95
96 return false;
97 }
98
99 return true;
100
101 }
102
103 static int _rmilter_pidfile_remove(rmilter_pidfh_t *pfh, int freeit);
104
105 static int
rmilter_pidfile_verify(rmilter_pidfh_t * pfh)106 rmilter_pidfile_verify(rmilter_pidfh_t *pfh)
107 {
108 struct stat sb;
109
110 if (pfh == NULL || pfh->pf_fd == -1)
111 return (-1);
112 /*
113 * Check remembered descriptor.
114 */
115 if (fstat (pfh->pf_fd, &sb) == -1)
116 return (errno);
117 if (sb.st_dev != pfh->pf_dev || sb.st_ino != pfh->pf_ino)
118 return -1;
119 return 0;
120 }
121
122 static int
rmilter_pidfile_read(const char * path,pid_t * pidptr)123 rmilter_pidfile_read(const char *path, pid_t * pidptr)
124 {
125 char buf[16], *endptr;
126 int error, fd, i;
127
128 fd = open (path, O_RDONLY);
129 if (fd == -1)
130 return (errno);
131
132 i = read (fd, buf, sizeof(buf) - 1);
133 error = errno; /* Remember errno in case close() wants to change it. */
134 close (fd);
135 if (i == -1)
136 return error;
137 else if (i == 0)
138 return EAGAIN;
139 buf[i] = '\0';
140
141 *pidptr = strtol (buf, &endptr, 10);
142 if (endptr != &buf[i])
143 return EINVAL;
144
145 return 0;
146 }
147
148 rmilter_pidfh_t *
rmilter_pidfile_open(const char * path,mode_t mode,pid_t * pidptr)149 rmilter_pidfile_open(const char *path, mode_t mode, pid_t * pidptr)
150 {
151 rmilter_pidfh_t *pfh;
152 struct stat sb;
153 int error, fd, len, count;
154 struct timespec rqtp;
155
156 pfh = malloc (sizeof(*pfh));
157 if (pfh == NULL)
158 return NULL;
159
160 if (path == NULL)
161 len = snprintf(pfh->pf_path, sizeof(pfh->pf_path), "/var/run/%s.pid",
162 _rmilter_progname);
163 else
164 len = snprintf(pfh->pf_path, sizeof(pfh->pf_path), "%s", path);
165 if (len >= (int) sizeof(pfh->pf_path)) {
166 free (pfh);
167 errno = ENAMETOOLONG;
168 return NULL;
169 }
170
171 /*
172 * Open the PID file and obtain exclusive lock.
173 * We truncate PID file here only to remove old PID immediatelly,
174 * PID file will be truncated again in pidfile_write(), so
175 * pidfile_write() can be called multiple times.
176 */
177 fd = open (pfh->pf_path, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, mode);
178 rmilter_file_lock (fd, true);
179 if (fd == -1) {
180 count = 0;
181 rqtp.tv_sec = 0;
182 rqtp.tv_nsec = 5000000;
183 if (errno == EWOULDBLOCK && pidptr != NULL) {
184 again:
185 errno = rmilter_pidfile_read (pfh->pf_path, pidptr);
186 if (errno == 0)
187 errno = EEXIST;
188 else if (errno == EAGAIN) {
189 if (++count <= 3) {
190 nanosleep (&rqtp, 0);
191 goto again;
192 }
193 }
194 }
195 free (pfh);
196 return NULL;
197 }
198 /*
199 * Remember file information, so in pidfile_write() we are sure we write
200 * to the proper descriptor.
201 */
202 if (fstat (fd, &sb) == -1) {
203 error = errno;
204 unlink (pfh->pf_path);
205 close (fd);
206 free (pfh);
207 errno = error;
208 return NULL;
209 }
210
211 pfh->pf_fd = fd;
212 pfh->pf_dev = sb.st_dev;
213 pfh->pf_ino = sb.st_ino;
214
215 return pfh;
216 }
217
218 int
rmilter_pidfile_write(rmilter_pidfh_t * pfh)219 rmilter_pidfile_write(rmilter_pidfh_t *pfh)
220 {
221 char pidstr[16];
222 int error, fd;
223
224 /*
225 * Check remembered descriptor, so we don't overwrite some other
226 * file if pidfile was closed and descriptor reused.
227 */
228 errno = rmilter_pidfile_verify (pfh);
229 if (errno != 0) {
230 /*
231 * Don't close descriptor, because we are not sure if it's ours.
232 */
233 return -1;
234 }
235 fd = pfh->pf_fd;
236
237 /*
238 * Truncate PID file, so multiple calls of pidfile_write() are allowed.
239 */
240 if (ftruncate (fd, 0) == -1) {
241 error = errno;
242 _rmilter_pidfile_remove (pfh, 0);
243 errno = error;
244 return -1;
245 }
246
247 snprintf (pidstr, sizeof(pidstr), "%ld", (long)getpid ());
248 if (pwrite (fd, pidstr, strlen (pidstr), 0) != (ssize_t) strlen (pidstr)) {
249 error = errno;
250 _rmilter_pidfile_remove (pfh, 0);
251 errno = error;
252 return -1;
253 }
254
255 return 0;
256 }
257
258 int
rmilter_pidfile_close(rmilter_pidfh_t * pfh)259 rmilter_pidfile_close(rmilter_pidfh_t *pfh)
260 {
261 int error;
262
263 error = rmilter_pidfile_verify (pfh);
264 if (error != 0) {
265 errno = error;
266 return -1;
267 }
268
269 if (close (pfh->pf_fd) == -1)
270 error = errno;
271 free (pfh);
272 if (error != 0) {
273 errno = error;
274 return -1;
275 }
276 return 0;
277 }
278
279 static int
_rmilter_pidfile_remove(rmilter_pidfh_t * pfh,int freeit)280 _rmilter_pidfile_remove(rmilter_pidfh_t *pfh, int freeit)
281 {
282 int error;
283
284 error = rmilter_pidfile_verify (pfh);
285 if (error != 0) {
286 errno = error;
287 return -1;
288 }
289
290 if (unlink (pfh->pf_path) == -1)
291 error = errno;
292 if (!rmilter_file_unlock (pfh->pf_fd, false)) {
293 if (error == 0)
294 error = errno;
295 }
296 if (close (pfh->pf_fd) == -1) {
297 if (error == 0)
298 error = errno;
299 }
300 if (freeit)
301 free (pfh);
302 else
303 pfh->pf_fd = -1;
304 if (error != 0) {
305 errno = error;
306 return -1;
307 }
308 return 0;
309 }
310
311 int
rmilter_pidfile_remove(rmilter_pidfh_t * pfh)312 rmilter_pidfile_remove(rmilter_pidfh_t *pfh)
313 {
314
315 return (_rmilter_pidfile_remove (pfh, 1));
316 }
317
318 /*
319 * Written by Manuel Bouyer <bouyer@NetBSD.org>.
320 * Public domain.
321 */
322 #ifndef bswap32
323 static uint32_t
bswap32(uint32_t x)324 bswap32 (uint32_t x)
325 {
326 return ((x << 24) & 0xff000000) |
327 ((x << 8) & 0x00ff0000) |
328 ((x >> 8) & 0x0000ff00) |
329 ((x >> 24) & 0x000000ff);
330 }
331 #endif
332
333 #ifndef bswap64
334 static uint64_t
bswap64(uint64_t x)335 bswap64 (uint64_t x)
336 {
337 #ifdef _LP64
338 /*
339 * Assume we have wide enough registers to do it without touching
340 * memory.
341 */
342 return ((x << 56) & 0xff00000000000000UL) |
343 ((x << 40) & 0x00ff000000000000UL) |
344 ((x << 24) & 0x0000ff0000000000UL) |
345 ((x << 8) & 0x000000ff00000000UL) |
346 ((x >> 8) & 0x00000000ff000000UL) |
347 ((x >> 24) & 0x0000000000ff0000UL) |
348 ((x >> 40) & 0x000000000000ff00UL) |
349 ((x >> 56) & 0x00000000000000ffUL);
350 #else
351 /*
352 * Split the operation in two 32bit steps.
353 */
354 uint32_t tl, th;
355
356 th = bswap32((uint32_t)(x & 0x00000000ffffffffULL));
357 tl = bswap32((uint32_t)((x >> 32) & 0x00000000ffffffffULL));
358 return ((uint64_t)th << 32) | tl;
359 #endif
360 }
361 #endif
362
363 static char *
rmilter_encode_base64_common(const u_char * in,size_t inlen,int str_len,size_t * outlen,int fold)364 rmilter_encode_base64_common (const u_char *in, size_t inlen, int str_len,
365 size_t *outlen, int fold)
366 {
367 #define CHECK_SPLIT \
368 do { if (str_len > 0 && cols >= str_len) { \
369 *o++ = '\r'; \
370 *o++ = '\n'; \
371 if (fold) *o++ = '\t'; \
372 cols = 0; \
373 } } \
374 while (0)
375
376 size_t allocated_len = (inlen / 3) * 4 + 5;
377 char *out, *o;
378 uint64_t n;
379 uint32_t rem, t, carry;
380 int cols, shift;
381 static const char b64_enc[] =
382 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
383 "abcdefghijklmnopqrstuvwxyz"
384 "0123456789+/";
385
386 if (str_len > 0) {
387 assert (str_len > 8);
388 allocated_len += (allocated_len / str_len + 1) * (fold ? 3 : 2) + 1;
389 }
390
391 out = malloc (allocated_len);
392 o = out;
393 cols = 0;
394
395 while (inlen > 6) {
396 n = *(uint64_t *) in;
397 #if BYTE_ORDER == LITTLE_ENDIAN
398 n = bswap64 (n);
399 #endif
400 if (str_len <= 0 || cols <= str_len - 8) {
401 *o++ = b64_enc[(n >> 58) & 0x3F];
402 *o++ = b64_enc[(n >> 52) & 0x3F];
403 *o++ = b64_enc[(n >> 46) & 0x3F];
404 *o++ = b64_enc[(n >> 40) & 0x3F];
405 *o++ = b64_enc[(n >> 34) & 0x3F];
406 *o++ = b64_enc[(n >> 28) & 0x3F];
407 *o++ = b64_enc[(n >> 22) & 0x3F];
408 *o++ = b64_enc[(n >> 16) & 0x3F];
409 cols += 8;
410 }
411 else {
412 cols = str_len - cols;
413 shift = 58;
414 while (cols) {
415 *o++ = b64_enc[(n >> shift) & 0x3F];
416 shift -= 6;
417 cols--;
418 }
419
420 *o++ = '\r';
421 *o++ = '\n';
422 if (fold) {
423 *o++ = '\t';
424 }
425
426 /* Remaining bytes */
427 while (shift >= 16) {
428 *o++ = b64_enc[(n >> shift) & 0x3F];
429 shift -= 6;
430 cols++;
431 }
432 }
433
434 in += 6;
435 inlen -= 6;
436 }
437
438 CHECK_SPLIT;
439
440 rem = 0;
441 carry = 0;
442
443 for (; ;) {
444 /* Padding + remaining data (0 - 2 bytes) */
445 switch (rem) {
446 case 0:
447 if (inlen-- == 0) {
448 goto end;
449 }
450 t = *in++;
451 *o++ = b64_enc[t >> 2];
452 carry = (t << 4) & 0x30;
453 rem = 1;
454 cols++;
455 case 1:
456 if (inlen-- == 0) {
457 goto end;
458 }
459 CHECK_SPLIT;
460 t = *in++;
461 *o++ = b64_enc[carry | (t >> 4)];
462 carry = (t << 2) & 0x3C;
463 rem = 2;
464 cols++;
465 default:
466 if (inlen-- == 0) {
467 goto end;
468 }
469 CHECK_SPLIT;
470 t = *in++;
471 *o++ = b64_enc[carry | (t >> 6)];
472 cols++;
473 CHECK_SPLIT;
474 *o++ = b64_enc[t & 0x3F];
475 cols++;
476 CHECK_SPLIT;
477 rem = 0;
478 }
479 }
480
481 end:
482 if (rem == 1) {
483 *o++ = b64_enc[carry];
484 cols++;
485 CHECK_SPLIT;
486 *o++ = '=';
487 cols++;
488 CHECK_SPLIT;
489 *o++ = '=';
490 cols++;
491 CHECK_SPLIT;
492 }
493 else if (rem == 2) {
494 *o++ = b64_enc[carry];
495 cols++;
496 CHECK_SPLIT;
497 *o++ = '=';
498 cols++;
499 }
500
501 CHECK_SPLIT;
502
503 *o = '\0';
504
505 if (outlen != NULL) {
506 *outlen = o - out;
507 }
508
509 return out;
510 }
511
512 char *
rmilter_encode_base64(const u_char * in,size_t inlen,int str_len,size_t * outlen)513 rmilter_encode_base64 (const u_char *in, size_t inlen, int str_len,
514 size_t *outlen)
515 {
516 return rmilter_encode_base64_common (in, inlen, str_len, outlen, 0);
517 }
518
519 int
rmilter_connect_addr(const char * addr,int port,int msec,const struct mlfi_priv * priv)520 rmilter_connect_addr (const char *addr, int port, int msec,
521 const struct mlfi_priv *priv)
522 {
523 struct sockaddr_un su;
524 int ofl, r;
525 struct addrinfo hints, *res, *res0;
526 int error;
527 int s;
528 const char *cause = NULL;
529 char portbuf[32];
530 socklen_t slen;
531
532 if (addr[0] == '/' || addr[0] == '.') {
533 /* Unix socket */
534 su.sun_family = AF_UNIX;
535 rmilter_strlcpy (su.sun_path, addr, sizeof (su.sun_path));
536 #if defined(FREEBSD) || defined(__APPLE__)
537 su.sun_len = SUN_LEN (&su);
538 #endif
539 s = socket (AF_UNIX, SOCK_STREAM, 0);
540 if (s < 0) {
541 cause = "socket";
542 error = errno;
543 }
544
545 ofl = fcntl (s, F_GETFL, 0);
546 fcntl (s, F_SETFL, ofl | O_NONBLOCK);
547 #ifdef SUN_LEN
548 slen = SUN_LEN (&su);
549 #else
550 slen = sizeof (su);
551 #endif
552
553 if (connect (s, (struct sockaddr *)&su, slen) < 0) {
554 if (errno != EINPROGRESS && errno != EAGAIN) {
555 cause = "connect";
556 error = errno;
557 close (s);
558 s = -1;
559 }
560 }
561 }
562 else {
563 memset(&hints, 0, sizeof(hints));
564 snprintf(portbuf, sizeof(portbuf), "%d", port);
565 hints.ai_family = PF_UNSPEC;
566 hints.ai_socktype = SOCK_STREAM;
567 hints.ai_flags = AI_NUMERICSERV;
568 error = getaddrinfo (addr, portbuf, &hints, &res0);
569
570 if (error) {
571 msg_err ("<%s>; rmilter_connect_addr: getaddrinfo failed for %s:%d: %s",
572 priv->mlfi_id, addr, port, gai_strerror (error));
573 return -1;
574 }
575
576 s = -1;
577 for (res = res0; res; res = res->ai_next) {
578 s = socket (res->ai_family, res->ai_socktype, res->ai_protocol);
579 if (s < 0) {
580 cause = "socket";
581 error = errno;
582 continue;
583 }
584
585 ofl = fcntl (s, F_GETFL, 0);
586 fcntl (s, F_SETFL, ofl | O_NONBLOCK);
587
588 if (connect (s, res->ai_addr, res->ai_addrlen) < 0) {
589 if (errno == EINPROGRESS || errno == EAGAIN) {
590 break;
591 }
592
593 cause = "connect";
594 error = errno;
595 close (s);
596 s = -1;
597 continue;
598 }
599
600 break; /* okay we got one */
601 }
602
603 freeaddrinfo (res0);
604 }
605
606 if (s < 0) {
607 if (s == 0) {
608 errno = ETIMEDOUT;
609 }
610
611 msg_err ("<%s>; rmilter_connect_addr: connect failed: %s: %s",
612 priv->mlfi_id, cause, strerror (error));
613 return -1;
614 }
615
616 /* Get write readiness */
617 if (rmilter_poll_fd (s, msec, POLLOUT) == 1) {
618 return s;
619 }
620 else {
621 msg_err ("<%s>; rmilter_connect_addr: connect failed: timeout", priv->mlfi_id);
622 close (s);
623 }
624
625 return -1;
626 }
627
628 int
rmilter_poll_fd(int fd,int timeout,short events)629 rmilter_poll_fd (int fd, int timeout, short events)
630 {
631 int r;
632 struct pollfd fds[1];
633
634 fds->fd = fd;
635 fds->events = events;
636 fds->revents = 0;
637 while ((r = poll (fds, 1, timeout)) < 0) {
638 if (errno != EINTR)
639 break;
640 }
641
642
643 return r;
644 }
645
646 static const unsigned char lc_map[256] = {
647 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
648 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
649 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
650 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
651 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
652 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
653 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
654 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
655 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
656 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
657 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
658 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
659 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
660 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
661 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
662 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
663 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
664 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
665 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
666 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
667 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
668 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
669 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
670 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
671 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
672 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
673 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
674 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
675 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
676 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
677 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
678 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
679 };
680
681 void
rmilter_str_lc(char * str,unsigned int size)682 rmilter_str_lc (char *str, unsigned int size)
683 {
684 unsigned int leftover = size % 4;
685 unsigned int fp, i;
686 const uint8_t* s = (const uint8_t*) str;
687 char *dest = str;
688 unsigned char c1, c2, c3, c4;
689
690 fp = size - leftover;
691
692 for (i = 0; i != fp; i += 4) {
693 c1 = s[i], c2 = s[i + 1], c3 = s[i + 2], c4 = s[i + 3];
694 dest[0] = lc_map[c1];
695 dest[1] = lc_map[c2];
696 dest[2] = lc_map[c3];
697 dest[3] = lc_map[c4];
698 dest += 4;
699 }
700
701 switch (leftover) {
702 case 3:
703 *dest++ = lc_map[(unsigned char)str[i++]];
704 case 2:
705 *dest++ = lc_map[(unsigned char)str[i++]];
706 case 1:
707 *dest++ = lc_map[(unsigned char)str[i]];
708 }
709
710 }
711
712 ssize_t
rmilter_atomic_write(int fd,const void * buf,size_t len)713 rmilter_atomic_write (int fd, const void *buf, size_t len)
714 {
715 const char *s = buf;
716 size_t pos = 0;
717 ssize_t res;
718
719 while (len > pos) {
720 res = write (fd, s + pos, len - pos);
721
722 switch (res) {
723 case -1:
724 if (errno == EINTR || errno == EAGAIN) {
725 continue;
726 }
727
728 return -1;
729 case 0:
730 errno = EPIPE;
731 return -1;
732 default:
733 pos += res;
734 }
735 }
736
737 return pos;
738 }
739
740 int
rmilter_file_xopen(const char * fname,int oflags,unsigned int mode)741 rmilter_file_xopen (const char *fname, int oflags, unsigned int mode)
742 {
743 struct stat sb;
744 int fd;
745
746 if (lstat (fname, &sb) == -1) {
747
748 if (errno != ENOENT) {
749 return (-1);
750 }
751 }
752 else if (!S_ISREG (sb.st_mode)) {
753 return -1;
754 }
755
756 #ifdef HAVE_ONOFOLLOW
757 fd = open (fname, oflags | O_NOFOLLOW, mode);
758 #else
759 fd = open (fname, oflags, mode);
760 #endif
761
762 return (fd);
763 }
764
765 void *
rmilter_file_xmap(const char * fname,unsigned int mode,size_t * size)766 rmilter_file_xmap (const char *fname, unsigned int mode,
767 size_t *size)
768 {
769 int fd;
770 struct stat sb;
771 void *map;
772
773 assert (fname != NULL);
774 assert (size != NULL);
775
776 if (mode & PROT_WRITE) {
777 fd = rmilter_file_xopen (fname, O_RDWR, 0);
778 }
779 else {
780 fd = rmilter_file_xopen (fname, O_RDONLY, 0);
781 }
782
783 if (fd == -1) {
784 return NULL;
785 }
786
787 if (fstat (fd, &sb) == -1 || !S_ISREG (sb.st_mode)) {
788 close (fd);
789
790 return NULL;
791 }
792
793 map = mmap (NULL, sb.st_size, mode, MAP_SHARED, fd, 0);
794 close (fd);
795
796 if (map == MAP_FAILED) {
797 return NULL;
798 }
799
800 *size = sb.st_size;
801
802 return map;
803 }
804
805 GString *
rmilter_header_value_fold(const gchar * name,const gchar * value,guint fold_max)806 rmilter_header_value_fold (const gchar *name,
807 const gchar *value,
808 guint fold_max)
809 {
810 GString *res;
811 const guint default_fold_max = 76;
812 guint cur_len;
813 const gchar *p, *c;
814 gboolean first_token = TRUE;
815 enum {
816 fold_before = 0,
817 fold_after
818 } fold_type = fold_before;
819 enum {
820 read_token = 0,
821 read_quoted,
822 after_quote,
823 fold_token,
824 } state = read_token, next_state = read_token;
825
826 g_assert (name != NULL);
827 g_assert (value != NULL);
828
829 /* Filter insane values */
830 if (fold_max < 20) {
831 fold_max = default_fold_max;
832 }
833
834 res = g_string_sized_new (strlen (value));
835
836 c = value;
837 p = c;
838 /* name:<WSP> */
839 cur_len = strlen (name) + 2;
840
841 while (*p) {
842 switch (state) {
843 case read_token:
844 if (*p == ',' || *p == ';') {
845 /* We have something similar to the token's end, so check len */
846 if (cur_len > fold_max * 0.8 && cur_len < fold_max) {
847 /* We want fold */
848 fold_type = fold_after;
849 state = fold_token;
850 next_state = read_token;
851 }
852 else if (cur_len > fold_max && !first_token) {
853 fold_type = fold_before;
854 state = fold_token;
855 next_state = read_token;
856 }
857 else {
858 g_string_append_len (res, c, p - c);
859 c = p;
860 first_token = FALSE;
861 }
862 p ++;
863 }
864 else if (*p == '"') {
865 /* Fold before quoted tokens */
866 g_string_append_len (res, c, p - c);
867 c = p;
868 state = read_quoted;
869 }
870 else if (*p == '\r') {
871 /* Reset line length */
872 cur_len = 0;
873
874 while (g_ascii_isspace (*p)) {
875 p ++;
876 }
877
878 g_string_append_len (res, c, p - c);
879 c = p;
880 }
881 else if (g_ascii_isspace (*p)) {
882 if (cur_len > fold_max * 0.8 && cur_len < fold_max) {
883 /* We want fold */
884 fold_type = fold_after;
885 state = fold_token;
886 next_state = read_token;
887 }
888 else if (cur_len > fold_max && !first_token) {
889 fold_type = fold_before;
890 state = fold_token;
891 next_state = read_token;
892 }
893 else {
894 g_string_append_len (res, c, p - c);
895 c = p;
896 first_token = FALSE;
897 p ++;
898 }
899 }
900 else {
901 p ++;
902 cur_len ++;
903 }
904 break;
905 case fold_token:
906 /* Here, we have token start at 'c' and token end at 'p' */
907 if (fold_type == fold_after) {
908 g_string_append_len (res, c, p - c);
909 g_string_append_len (res, "\r\n\t", 3);
910
911 /* Skip space if needed */
912 if (g_ascii_isspace (*p)) {
913 p ++;
914 }
915 }
916 else {
917 /* Skip space if needed */
918 if (g_ascii_isspace (*c)) {
919 c ++;
920 }
921
922 g_string_append_len (res, "\r\n\t", 3);
923 g_string_append_len (res, c, p - c);
924 }
925
926 c = p;
927 state = next_state;
928 cur_len = 0;
929 first_token = TRUE;
930 break;
931
932 case read_quoted:
933 if (p != c && *p == '"') {
934 state = after_quote;
935 }
936 p ++;
937 cur_len ++;
938 break;
939
940 case after_quote:
941 state = read_token;
942 /* Skip one more character after the quote */
943 p ++;
944 cur_len ++;
945 g_string_append_len (res, c, p - c);
946 c = p;
947 first_token = TRUE;
948 break;
949 }
950 }
951
952 /* Last token */
953 switch (state) {
954 case read_token:
955 if (cur_len > fold_max && !first_token) {
956 g_string_append_len (res, "\r\n\t", 3);
957 g_string_append_len (res, c, p - c);
958 }
959 else {
960 g_string_append_len (res, c, p - c);
961 }
962 break;
963 case read_quoted:
964 case after_quote:
965 g_string_append_len (res, c, p - c);
966 break;
967
968 default:
969 g_assert (p == c);
970 break;
971 }
972
973 return res;
974 }
975