head 1.92; access; symbols milter-greylist-4-5-13:1.91 milter-greylist-4-5-12:1.90 milter-greylist-4-5-11:1.90 milter-greylist-4-5-10:1.90 milter-greylist-4-9-10:1.90 milter-greylist-4-5-8:1.90 milter-greylist-4-5-9:1.90 milter-greylist-4-5-7:1.90 milter-greylist-4-5-6:1.90 milter-greylist-4-5:1.90 milter-greylist-4-5-5:1.90 milter-greylist-4-5-4:1.90 milter-greylist-4-5-3:1.90 milter-greylist-4-5-2:1.90 milter-greylist-4-5-1:1.90 milter-greylist-4-4-3:1.89 milter-greylist-4-4-2:1.89 milter-greylist-4-4-1:1.89 milter-greylist-4-4:1.89 milter-greylist-4-4-rc1:1.89 milter-greylist-4-4-alpha4:1.89 milter-greylist-4-4-alpha3:1.89 milter-greylist-4-4-alpha2:1.89 milter-greylist-4-4-alpha1:1.89 milter-greylist-4-2-7:1.82.2.2 milter-greylist-4-3-9:1.89 milter-greylist-4-2-6:1.82.2.2 milter-gresylit-4-2-6:1.82.2.2 milter-greylist-4-3-8:1.89 milter-greylist-4-3-7:1.88 milter-greylist-4-2-5:1.82.2.2 milter-greylist-4-3-6:1.88 milter-greylist-4-2-4:1.82.2.1 milter-greylist-4-3-5:1.88 milter-greylist-4-3-4:1.86 milter-greylist-4-2-3:1.82.2.1 milter-greylist-4-3-3:1.86 rmilter-greylist-4-2-3:1.82.2.1 milter-greylist-4-3-2:1.84 milter-greylist-4-3-1:1.83 milter-greylist-2-2-2:1.82.2.1 milter-greylist-4-2-2:1.82.2.1 milter-greylist-4-2-1:1.82 milter-greylist-4-2:1.82 milter-greylist-4-2-rc1:1.82 milter-greylist-4-2-beta1:1.82 milter-greylist-4-2-branch:1.82.0.2 milter-greylist-4-2-base:1.82 milter-greylist-4-2-0-base:1.82 milter-greylist-4-1-12:1.82 milter-greylist-4-1-11:1.82 milter-greylist-4-1-10:1.82 milter-greylist-4-1-9:1.82 milter-greylist-4-1-8:1.81 milter-greylist-4-1-7:1.81 milter-greylist-4-1-6:1.81 milter-greylist-4-0-1:1.80 milter-greylist-4-0-1-rc1:1.80 milter-greylist-4-1-5:1.81 milter-greylist-4-1-4:1.81 milter-greylist-4-1-3:1.81 milter-greylist-4-1-2:1.81 milter-greylist-4-1-1:1.80 milter-greylist-4-0-branch:1.80.0.2 milter-greylist-4-0-base:1.80 milter-greylist-4-0:1.80 milter-greylist-4-0-rc2:1.80 milter-greylist-4-0-rc1:1.80 milter-greylist-4-0-beta4:1.80 milter-greylist-4-0-beta3:1.76 milter-greylist-4-0-beta2:1.72 milter-greylist-4-0-beta1:1.71 milter-greylist-4-0-alpha6:1.70 milter-greylist-4-0-alpha5:1.70 milter-greylist-4-0-alpha4:1.70 milter-greylist-4-0-alpha3:1.70 milter-greylist-4-0-alpha2:1.70 milter-greylist-4-0-alpha1:1.70 milter-greylist-3-1-8:1.70 milter-greylist-3-1-7:1.70 milter-greylist-3-1-6:1.69 milter-greylist-3-1-5:1.69 milter-greylist-3-1-5-alpha1:1.69 milter-greylist-3-0-1-beta1:1.66.2.5 milter-greylist-3-1-4:1.69 milter-greylist-3-1-3:1.69 milter-greylist-3-1-2:1.69 milter-greylist-3-1-1:1.69 milter-greylist-3-0:1.66.2.5 milter-greylist-3-0-rc7:1.66.2.5 milter-greylist-3-0-rc6:1.66.2.4 milter-greylist-3-0-rc5:1.66.2.4 milter-greylist-3-0-rc4:1.66.2.3 milter-greylist-3-0-rc3:1.66.2.2 milter-greylist-3-0-rc2:1.66.2.2 milter-greylist-3-0-rc1:1.66.2.1 milter-greylist-3-0-alpha6:1.66.2.1 milter-greylist-3-0-branch:1.66.0.2 milter-greylist-3-0-base:1.66 milter-greylist-3-0-alpha5:1.66 milter-greylist-3-0-alpha4:1.63 milter-greylist-3-0-alpha3:1.63 milter-greylist-3-0-alpha2:1.62 milter-greylist-3-0-alpha1:1.61 milter-greylist-2-1-12:1.61 milter-greylist-2-1-11:1.61 milter-greylist-2-1-10:1.60 milter-greylist-2-1-9:1.59 milter-greylist-2-1-9a1:1.59 milter-greylist-2-1-8:1.59 milter-greylist-2-1-7:1.59 milter-greylist-2-1-6:1.59 milter-greylist-2-1-5:1.58 milter-greylist-2-1-4:1.58 milter-greylist-2-1-3:1.58 milter-greylist-2-1-2:1.57 milter-greylist-2-1-1:1.57 milter-greylist-2-0-2:1.56 milter-greylist-2-0-1:1.55 milter-greylist-2-0-1-b1:1.55 milter-greylist-2-0-release:1.55 milter-greylist-2-0-rc5:1.55 milter-greylist-2-0-rc4:1.55 milter-greylist-2-0-rc3:1.55 milter-grey-list-2-0-rc3:1.55 milter-grey-list-2-0-rc2:1.55 milter-grey-list-2-0-rc1:1.55 milter-greylist-2-0-beta7:1.55 milter-greylist-2-0-beta6:1.55 milter-gre-ylist-2-0-beta5:1.55 milter-greylist-2-0-beta5:1.55 milter-greylist-2-0-beta4:1.55 milter-greylist-2-0-beta3:1.55 milter-greylist-2-0-beta2:1.55 milter-greylist-2-0:1.55.0.2 milter-greylist-2-0-base:1.55 milter-greylist-2-0-beta1:1.55 milter-greylist-1-7-5:1.55 before_delayed_tempfail:1.55 milter-greylist-1-7-4:1.53 milter-greylist-1-7-3:1.53 milter-greylist-1-7-2:1.53 milter-greylist-1-6-0:1.52 milter-greylist-1-7-1:1.52 milter-greylist-1-6rc1:1.52 milter-greylist-1-6:1.52.0.2 milter-greylist-1-6-base:1.52 milter-greylist-1-5-12:1.52 milter-greylist-1-5-11:1.50 milter-greylist-1-5-10:1.49 milter-greylist-1-5-9:1.49 milter-greylist-1-5-8:1.49 milter-greylist-1-5-7:1.49 milter-greylist-1-5-6:1.49 milter-greylist-1-5-5:1.47 milter-greylist-1-5-4:1.47 milter-greylist-1-5-3:1.46 milter-greylist-1-5-2:1.45 milter-greylist-1-5-1:1.43 milter-greylist-1-4:1.42.0.2 milter-greylist-1-4-base:1.42 milter-greylist-1-3-9:1.42 milter-greylist-1-3-8:1.40 milter-greylist-1-3-7:1.40 milter-greylist-1-3-6:1.39 milter-greylist-1-3-5:1.39 milter-greylist-1-3-4:1.39 milter-greylist-1-3-3:1.39 BDB:1.38.0.2 BDB-base:1.38 before_BDB:1.37 milter-greylist-1-2-2:1.36.2.1 milter-greylist-1-3-2:1.36 milter-greylist-1-2-1:1.36 milter-greylist-1-2-0:1.36 milter-greylist-1-2:1.36.0.2 milter-greylist-1-2-base:1.36 milter-greylist-1-1-16:1.36 milter-greylist-1-1-15:1.36 milter-greylis-1-1-15:1.36 milter-greylis-1-1-16:1.36 milter-greylist-1-1-14:1.36 milter-greylist-1-1-13:1.36 milter-greylist-1-1-12:1.36 milter-greylist-1-1-11:1.36 milter-greylist-1-1-10:1.36 milter-greylist-1-10rc1:1.36 milter-greylist-1-1-9:1.36 milter-greylist-1-1-8:1.36 milter-greylist-1-1-7:1.36 milter-greylist-1-1-6:1.36 milter-greylist-1-1-5:1.35 milter-greylist-1-1-4:1.35 milter-greylist-1-1-3:1.34 milter-greylist-1-1-2:1.34 milter-greylist-1-0-2:1.33 rmilter-greylist-1-0-1:1.33 milter-greylist-1-0-1:1.33 milter-greylist-1-1-1:1.33 milter-greylist-1-0-base:1.33 milter-greylist-1-0:1.33.0.2 milter-greylist-1-0-0:1.33 milter-greylist-0-27:1.33 milter-greylist-0-26:1.33 milter-greylist-0-25:1.33 milter-greylist-0-24:1.31 milter-greylist-0-23:1.31 milter-greylist-0-22:1.27 milter-greylist-0-21:1.26 milter-greylist-0-20:1.25 milter-greylist-0-19:1.25 milter-greylist-0-18:1.20 milter-greylist-0-17:1.20 milter-greylist-0-16:1.15 milter-greylist-0-15:1.15 milter-greylist-0-14:1.11 milter-greylist-0-13:1.7; locks; strict; comment @ * @; 1.92 date 2015.06.18.03.42.48; author manu; state Exp; branches; next 1.91; 1.91 date 2015.06.08.19.05.50; author manu; state Exp; branches; next 1.90; 1.90 date 2013.03.23.01.45.02; author manu; state Exp; branches; next 1.89; 1.89 date 2010.06.16.01.30.30; author manu; state Exp; branches; next 1.88; 1.88 date 2009.10.31.21.28.03; author manu; state Exp; branches; next 1.87; 1.87 date 2009.10.11.11.26.22; author manu; state Exp; branches; next 1.86; 1.86 date 2009.05.23.22.50.53; author manu; state Exp; branches; next 1.85; 1.85 date 2009.05.14.19.15.37; author manu; state Exp; branches; next 1.84; 1.84 date 2009.04.19.00.55.32; author manu; state Exp; branches; next 1.83; 1.83 date 2009.04.04.03.09.43; author manu; state Exp; branches; next 1.82; 1.82 date 2009.01.17.04.32.55; author manu; state Exp; branches 1.82.2.1; next 1.81; 1.81 date 2008.06.03.10.26.19; author manu; state Exp; branches; next 1.80; 1.80 date 2007.10.05.23.12.47; author manu; state Exp; branches; next 1.79; 1.79 date 2007.10.05.10.35.00; author manu; state Exp; branches; next 1.78; 1.78 date 2007.10.04.16.27.52; author manu; state Exp; branches; next 1.77; 1.77 date 2007.10.03.16.28.50; author manu; state Exp; branches; next 1.76; 1.76 date 2007.10.03.10.52.23; author manu; state Exp; branches; next 1.75; 1.75 date 2007.09.27.10.43.49; author manu; state Exp; branches; next 1.74; 1.74 date 2007.09.27.03.45.03; author manu; state Exp; branches; next 1.73; 1.73 date 2007.09.27.03.37.50; author manu; state Exp; branches; next 1.72; 1.72 date 2007.08.24.03.32.49; author manu; state Exp; branches; next 1.71; 1.71 date 2007.07.08.21.02.28; author manu; state Exp; branches; next 1.70; 1.70 date 2007.03.16.03.58.04; author manu; state Exp; branches; next 1.69; 1.69 date 2006.11.07.05.12.01; author manu; state Exp; branches; next 1.68; 1.68 date 2006.10.10.20.28.43; author manu; state Exp; branches; next 1.67; 1.67 date 2006.09.27.11.54.07; author manu; state Exp; branches; next 1.66; 1.66 date 2006.08.30.20.50.42; author manu; state Exp; branches 1.66.2.1; next 1.65; 1.65 date 2006.08.30.04.57.58; author manu; state Exp; branches; next 1.64; 1.64 date 2006.08.28.22.28.35; author manu; state Exp; branches; next 1.63; 1.63 date 2006.08.27.20.54.41; author manu; state Exp; branches; next 1.62; 1.62 date 2006.08.20.05.53.26; author manu; state Exp; branches; next 1.61; 1.61 date 2006.08.01.14.55.20; author manu; state Exp; branches; next 1.60; 1.60 date 2006.07.30.19.52.51; author manu; state Exp; branches; next 1.59; 1.59 date 2006.07.24.22.49.43; author manu; state Exp; branches; next 1.58; 1.58 date 2006.01.11.06.40.39; author manu; state Exp; branches; next 1.57; 1.57 date 2006.01.08.00.38.25; author manu; state Exp; branches; next 1.56; 1.56 date 2005.10.20.07.24.53; author manu; state Exp; branches; next 1.55; 1.55 date 2005.01.22.17.23.17; author manu; state Exp; branches; next 1.54; 1.54 date 2005.01.22.13.00.39; author manu; state Exp; branches; next 1.53; 1.53 date 2004.12.08.22.23.09; author manu; state Exp; branches; next 1.52; 1.52 date 2004.11.12.14.22.22; author manu; state Exp; branches; next 1.51; 1.51 date 2004.11.11.08.51.02; author manu; state Exp; branches; next 1.50; 1.50 date 2004.10.15.19.17.39; author manu; state Exp; branches; next 1.49; 1.49 date 2004.08.09.20.26.52; author manu; state Exp; branches; next 1.48; 1.48 date 2004.08.08.21.24.20; author manu; state Exp; branches; next 1.47; 1.47 date 2004.08.01.09.27.03; author manu; state Exp; branches; next 1.46; 1.46 date 2004.06.17.21.13.37; author manu; state Exp; branches; next 1.45; 1.45 date 2004.06.17.19.57.51; author manu; state Exp; branches; next 1.44; 1.44 date 2004.06.17.19.57.33; author manu; state Exp; branches; next 1.43; 1.43 date 2004.06.16.21.00.39; author manu; state Exp; branches; next 1.42; 1.42 date 2004.06.08.14.47.47; author manu; state Exp; branches; next 1.41; 1.41 date 2004.06.08.12.09.36; author manu; state Exp; branches; next 1.40; 1.40 date 2004.05.31.11.19.57; author manu; state Exp; branches; next 1.39; 1.39 date 2004.05.24.21.22.03; author manu; state Exp; branches; next 1.38; 1.38 date 2004.05.15.08.41.54; author manu; state Exp; branches; next 1.37; 1.37 date 2004.05.06.13.50.55; author manu; state Exp; branches; next 1.36; 1.36 date 2004.04.01.14.03.52; author manu; state Exp; branches 1.36.2.1; next 1.35; 1.35 date 2004.03.31.09.49.16; author manu; state Exp; branches; next 1.34; 1.34 date 2004.03.30.11.43.46; author manu; state Exp; branches; next 1.33; 1.33 date 2004.03.22.23.29.59; author manu; state Exp; branches; next 1.32; 1.32 date 2004.03.22.21.56.35; author manu; state Exp; branches; next 1.31; 1.31 date 2004.03.22.07.12.38; author manu; state Exp; branches; next 1.30; 1.30 date 2004.03.22.07.01.53; author manu; state Exp; branches; next 1.29; 1.29 date 2004.03.22.07.00.26; author manu; state Exp; branches; next 1.28; 1.28 date 2004.03.21.23.21.13; author manu; state Exp; branches; next 1.27; 1.27 date 2004.03.20.11.20.05; author manu; state Exp; branches; next 1.26; 1.26 date 2004.03.20.07.19.03; author manu; state Exp; branches; next 1.25; 1.25 date 2004.03.17.22.28.57; author manu; state Exp; branches; next 1.24; 1.24 date 2004.03.17.17.33.40; author manu; state Exp; branches; next 1.23; 1.23 date 2004.03.17.13.22.44; author manu; state Exp; branches; next 1.22; 1.22 date 2004.03.16.23.16.52; author manu; state Exp; branches; next 1.21; 1.21 date 2004.03.16.21.58.34; author manu; state Exp; branches; next 1.20; 1.20 date 2004.03.14.15.43.33; author manu; state Exp; branches; next 1.19; 1.19 date 2004.03.14.13.47.49; author manu; state Exp; branches; next 1.18; 1.18 date 2004.03.14.11.42.22; author manu; state Exp; branches; next 1.17; 1.17 date 2004.03.14.11.05.53; author manu; state Exp; branches; next 1.16; 1.16 date 2004.03.13.16.04.39; author manu; state Exp; branches; next 1.15; 1.15 date 2004.03.12.10.27.56; author manu; state Exp; branches; next 1.14; 1.14 date 2004.03.12.10.03.11; author manu; state Exp; branches; next 1.13; 1.13 date 2004.03.12.07.52.06; author manu; state Exp; branches; next 1.12; 1.12 date 2004.03.11.18.38.48; author manu; state Exp; branches; next 1.11; 1.11 date 2004.03.11.17.32.57; author manu; state Exp; branches; next 1.10; 1.10 date 2004.03.11.17.02.11; author manu; state Exp; branches; next 1.9; 1.9 date 2004.03.11.16.57.49; author manu; state Exp; branches; next 1.8; 1.8 date 2004.03.11.16.33.44; author manu; state Exp; branches; next 1.7; 1.7 date 2004.03.11.14.12.48; author manu; state Exp; branches; next 1.6; 1.6 date 2004.03.10.21.11.45; author manu; state Exp; branches; next 1.5; 1.5 date 2004.03.10.20.36.29; author manu; state Exp; branches; next 1.4; 1.4 date 2004.03.10.16.07.07; author manu; state Exp; branches; next 1.3; 1.3 date 2004.03.10.15.57.18; author manu; state Exp; branches; next 1.2; 1.2 date 2004.03.10.14.24.34; author manu; state Exp; branches; next 1.1; 1.1 date 2004.03.10.14.17.14; author manu; state Exp; branches; next ; 1.82.2.1 date 2009.04.04.03.20.26; author manu; state Exp; branches; next 1.82.2.2; 1.82.2.2 date 2010.04.17.03.19.02; author manu; state Exp; branches; next ; 1.66.2.1 date 2006.09.04.22.05.59; author manu; state Exp; branches; next 1.66.2.2; 1.66.2.2 date 2006.09.20.09.05.56; author manu; state Exp; branches; next 1.66.2.3; 1.66.2.3 date 2006.09.27.11.54.47; author manu; state Exp; branches; next 1.66.2.4; 1.66.2.4 date 2006.10.10.20.28.15; author manu; state Exp; branches; next 1.66.2.5; 1.66.2.5 date 2006.11.07.05.12.12; author manu; state Exp; branches; next ; 1.36.2.1 date 2004.05.06.13.54.01; author manu; state Exp; branches; next ; desc @@ 1.92 log @Build fixes for INFTIM and FNM_CASEFOLD @ text @/* $Id: sync.c,v 1.91 2015/06/08 19:05:50 manu Exp $ */ /* * Copyright (c) 2004-2010 Emmanuel Dreyfus * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Emmanuel Dreyfus * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #ifdef HAVE_SYS_CDEFS_H #include #ifdef __RCSID __RCSID("$Id: sync.c,v 1.91 2015/06/08 19:05:50 manu Exp $"); #endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pending.h" #include "sync.h" #include "conf.h" #include "milter-greylist.h" #ifdef USE_DMALLOC #include #endif #ifndef INFTIM #define INFTIM -1 /* non standard */ #endif struct pending *pending_get(struct sockaddr *, socklen_t, char *, char *, time_t, time_t, tuple_t); void pending_del(struct sockaddr *, socklen_t, char *, char *, time_t, time_t); void pending_del_addr(struct sockaddr *, socklen_t, char *, int); struct pending *pending_ref(struct pending *); void pending_free(struct pending *); #define SYNC_PROTO_CURRENT 3 struct sync_master_sock { int runs; int sock; }; #define SMS_RUNNING 1 #define SMS_NOTRUNNING 0 #define SMS_DISABLED -1 /* errors returned by stdio that should not cause an exit */ static int sync_ignored_errno[] = { #ifdef EAGAIN EAGAIN, #endif /* EAGAIN */ #ifdef ECONNABORTED ECONNABORTED, #endif /* ECONNABORTED */ #ifdef EMFILE EMFILE, #endif /* EMFILE */ #ifdef ENFILE ENFILE, #endif /* ENFILE */ #ifdef ENOBUFS ENOBUFS, #endif /* ENOBUFS */ #ifdef ENOMEM ENOMEM, #endif /* ENOMEM */ #ifdef ENOSR ENOSR, #endif /* ENOSR */ #ifdef EWOULDBLOCK EWOULDBLOCK, #endif /* EWOULDBLOCK */ 0, }; static pthread_mutex_t sync_master_lock = PTHREAD_MUTEX_INITIALIZER; struct sync_master_sock sync_master4 = { 0, -1 }; struct sync_master_sock sync_master6 = { 0, -1 }; struct peerlist peer_head; pthread_rwlock_t peer_lock; /* For the peer list */ static pthread_mutex_t sync_dirty_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t sync_sleepflag = PTHREAD_COND_INITIALIZER; static int sync_dirty = 0; static void sync_listen(char *, char *, struct sync_master_sock *); static int local_addr(const struct sockaddr *sa, const socklen_t salen); static int sync_queue_poke(struct peer *, struct sync *); static struct sync * sync_queue_peek(struct peer *); static int select_protocol(struct peer *, int, FILE *); static void sync_vers(FILE *, int); static int is_fatal(int); void peer_init(void) { int error; LIST_INIT(&peer_head); if ((error = pthread_rwlock_init(&peer_lock, NULL)) != 0) { mg_log(LOG_ERR, "pthread_rwlock_init failed: %s", strerror(error)); exit(EX_OSERR); } return; } void peer_clear(void) { struct peer *peer; struct sync *sync; PEER_WRLOCK; while(!LIST_EMPTY(&peer_head)) { peer = LIST_FIRST(&peer_head); while((sync = sync_queue_peek(peer)) != NULL) sync_free(sync); if (peer->p_stream != NULL) Fclose(peer->p_stream); LIST_REMOVE(peer, p_list); pthread_mutex_destroy(&peer->p_mtx); free(peer->p_name); free(peer); } PEER_UNLOCK; return; } void peer_add(peername, sockettimeout) char *peername; time_t sockettimeout; { struct peer *peer; if ((peer = malloc(sizeof(*peer))) == NULL || (peer->p_name = strdup(peername)) == NULL) { mg_log(LOG_ERR, "cannot add peer: %s", strerror(errno)); exit(EX_OSERR); } peer->p_qlen = 0; peer->p_stream = NULL; peer->p_flags = 0; peer->p_socket_timeout = sockettimeout; TAILQ_INIT(&peer->p_deferred); pthread_mutex_init(&peer->p_mtx, NULL); PEER_WRLOCK; LIST_INSERT_HEAD(&peer_head, peer, p_list); PEER_UNLOCK; if (conf.c_debug) mg_log(LOG_DEBUG, "load peer %s", peer->p_name); return; } void peer_create(pending) struct pending *pending; { struct peer *peer; PEER_RDLOCK; if (LIST_EMPTY(&peer_head)) goto out; LIST_FOREACH(peer, &peer_head, p_list) sync_queue(peer, PS_CREATE, pending, -1); /* -1: unused */ out: PEER_UNLOCK; return; } void peer_delete(pending, autowhite) struct pending *pending; time_t autowhite; { struct peer *peer; PEER_RDLOCK; if (LIST_EMPTY(&peer_head)) goto out; LIST_FOREACH(peer, &peer_head, p_list) sync_queue(peer, PS_DELETE, pending, autowhite); out: PEER_UNLOCK; return; } static int sync_queue_poke(peer, sync) struct peer *peer; struct sync *sync; { int r = 0; pthread_mutex_lock(&peer->p_mtx); if (peer->p_qlen < conf.c_syncmaxqlen) { TAILQ_INSERT_HEAD(&peer->p_deferred, sync, s_list); peer->p_qlen++; r = 1; } pthread_mutex_unlock(&peer->p_mtx); return r; } static struct sync * sync_queue_peek(peer) struct peer *peer; { struct sync *sync; pthread_mutex_lock(&peer->p_mtx); sync = TAILQ_FIRST(&peer->p_deferred); if (!TAILQ_EMPTY(&peer->p_deferred)) { TAILQ_REMOVE(&peer->p_deferred, sync, s_list); peer->p_qlen--; } pthread_mutex_unlock(&peer->p_mtx); return sync; } int sync_send(peer, type, pending, autowhite) /* peer list is read-locked */ struct peer *peer; peer_sync_t type; struct pending *pending; time_t autowhite; { char sep[] = " \n\t\r"; char *replystr; int replycode; char line[LINELEN + 1]; char *cookie = NULL; char *keyw; char awstr[LINELEN + 1]; int bw; int errcount = 0; if ((peer->p_stream == NULL) && (peer_connect(peer) != 0)) return -1; *line = '\0'; switch(type) { case PS_FLUSH: bw = snprintf(line, LINELEN, "flush addr %s\r\n", pending->p_addr); break; case PS_CREATE: bw = snprintf(line, LINELEN, "add addr %s from %s " "rcpt %s date %ld\r\n", pending->p_addr, pending->p_from, pending->p_rcpt, (long)pending->p_tv.tv_sec); break; default: if (peer->p_vers >= 2) { keyw = "del2"; snprintf(awstr, LINELEN, " aw %ld", (long)autowhite); } else { keyw = "del"; awstr[0] = '\0'; } bw = snprintf(line, LINELEN, "%s addr %s from %s " "rcpt %s date %ld%s\r\n", keyw, pending->p_addr, pending->p_from, pending->p_rcpt, (long)pending->p_tv.tv_sec, awstr); break; } if (bw > LINELEN) { mg_log(LOG_ERR, "closing connection with peer %s: " "send buffer would overflow (%d entries queued)", peer->p_name, peer->p_qlen); Fclose(peer->p_stream); peer->p_stream = NULL; return -1; } bw = fprintf(peer->p_stream, "%s", line); if (bw != strlen(line)) { mg_log(LOG_ERR, "closing connection with peer %s: " "%s (%d entries queued) - I was unable to send " "complete line \"%s\" - bytes written: %i", peer->p_name, strerror(errno), peer->p_qlen, line, bw); Fclose(peer->p_stream); peer->p_stream = NULL; return -1; } fflush(peer->p_stream); /* * Check the return code */ get_more: sync_waitdata(peer->p_socket, peer->p_socket_timeout); if (fgets(line, LINELEN, peer->p_stream) == NULL) { if (errno == EAGAIN) { errcount++; if ( feof(peer->p_stream) ) { mg_log(LOG_ERR, "lost connection with peer %s: " "%s (%d entries queued)", peer->p_name, strerror(errno), peer->p_qlen); Fclose(peer->p_stream); peer->p_stream = NULL; return -1; } if (errcount < 5) { goto get_more; } else { mg_log(LOG_ERR, "too many read error with peer %s", peer->p_name); } } mg_log(LOG_ERR, "lost connection with peer %s: " "%s (%d entries queued)", peer->p_name, strerror(errno), peer->p_qlen); Fclose(peer->p_stream); peer->p_stream = NULL; return -1; } /* * On some systems, opening a stream on a socket introduce * weird behavior: the in and out buffers get mixed up. * By calling fflush() after each read operation, we fix that */ fflush(peer->p_stream); if ((replystr = strtok_r(line, sep, &cookie)) == NULL) { mg_log(LOG_ERR, "Unexpected reply \"%s\" from %s, " "closing connection (%d entries queued)", line, peer->p_name, peer->p_qlen); Fclose(peer->p_stream); peer->p_stream = NULL; return -1; } replycode = atoi(replystr); if (replycode != 201) { mg_log(LOG_ERR, "Unexpected reply \"%s\" from %s, " "closing connection (%d entries queued)", line, peer->p_name, peer->p_qlen); Fclose(peer->p_stream); peer->p_stream = NULL; return -1; } if (conf.c_debug) mg_log(LOG_DEBUG, "sync one entry with %s", peer->p_name); return 0; } int peer_connect(peer) /* peer list is read-locked */ struct peer *peer; { struct servent *se; #ifdef HAVE_GETADDRINFO struct addrinfo hints, *res0, *res; int err; #else struct protoent *pe; int proto; sockaddr_t raddr; socklen_t raddrlen; #endif sockaddr_t laddr; socklen_t laddrlen; char *laddrstr; int service; int s = -1; char *replystr; int replycode; FILE *stream; char sep[] = " \n\t\r"; char line[LINELEN + 1]; int param; char *cookie = NULL; if (peer->p_stream != NULL) mg_log(LOG_ERR, "peer_connect called and peer->p_stream != 0"); if (conf.c_syncport != NULL) { service = htons(atoi(conf.c_syncport)); } else { if ((se = getservbyname(MXGLSYNC_NAME, "tcp")) == NULL) service = htons(atoi(MXGLSYNC_PORT)); else service = se->s_port; } #ifdef HAVE_GETADDRINFO bzero(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ((err = getaddrinfo(peer->p_name, "0", &hints, &res0)) != 0) { mg_log(LOG_ERR, "cannot sync with peer %s, " "getaddrinfo failed: %s (%d entries queued)", peer->p_name, gai_strerror(err), peer->p_qlen); return -1; } for (res = res0; res; res = res->ai_next) { /* We only test an address family which kernel supports. */ s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s == -1) continue; close(s); if (local_addr(res->ai_addr, res->ai_addrlen)) { peer->p_flags |= P_LOCAL; freeaddrinfo(res0); return -1; } } for (res = res0; res; res = res->ai_next) { s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s == -1) continue; SET_CLOEXEC(s); switch (res->ai_family) { case AF_INET: SA4(res->ai_addr)->sin_port = service; if (conf.c_syncsrcaddr != NULL) { laddrstr = conf.c_syncsrcaddr; } else { laddrstr = "0.0.0.0"; } break; #ifdef AF_INET6 case AF_INET6: SA6(res->ai_addr)->sin6_port = service; if (conf.c_syncsrcaddr != NULL) { laddrstr = conf.c_syncsrcaddr; } else { laddrstr = "::"; } break; #endif default: mg_log(LOG_ERR, "cannot sync, unknown address family"); close(s); s = -1; continue; } laddrlen = sizeof(laddr); if (ipfromstring(laddrstr, SA(&laddr), &laddrlen, res->ai_family) == 1 && bind(s, SA(&laddr), laddrlen) == 0 && connect(s, res->ai_addr, res->ai_addrlen) == 0) break; close(s); s = -1; } freeaddrinfo(res0); if (s < 0) { mg_log(LOG_ERR, "cannot sync with peer %s: %s (%d entries queued)", peer->p_name, strerror(errno), peer->p_qlen); return -1; } #else raddrlen = sizeof(raddr); if (ipfromstring(peer->p_name, SA(&raddr), &raddrlen, AF_UNSPEC) != 1) { mg_log(LOG_ERR, "cannot sync, invalid address"); return -1; } if (local_addr(SA(&raddr), raddrlen)) { peer->p_flags |= P_LOCAL; return -1; } switch (SA(&raddr)->sa_family) { case AF_INET: SA4(&raddr)->sin_port = service; if (conf.c_syncsrcaddr != NULL) { laddrstr = conf.c_syncsrcaddr; } else { laddrstr = "0.0.0.0"; } break; #ifdef AF_INET6 case AF_INET6: SA6(&raddr)->sin6_port = service; laddrstr = "::"; break; #endif default: mg_log(LOG_ERR, "cannot sync, unknown address family"); return -1; } if ((pe = getprotobyname("tcp")) == NULL) proto = 6; else proto = pe->p_proto; if ((s = socket(SA(&raddr)->sa_family, SOCK_STREAM, proto)) == -1) { mg_log(LOG_ERR, "cannot sync with peer %s, " "socket failed: %s (%d entries queued)", peer->p_name, strerror(errno), peer->p_qlen); return -1; } SET_CLOEXEC(s); laddrlen = sizeof(laddr); if (ipfromstring(laddrstr, SA(&laddr), &laddrlen, SA(&raddr)->sa_family) != 1) { mg_log(LOG_ERR, "cannot sync, invalid address"); close(s); return -1; } if (bind(s, SA(&laddr), laddrlen) != 0) { mg_log(LOG_ERR, "cannot sync with peer %s, " "bind failed: %s (%d entries queued)", peer->p_name, strerror(errno), peer->p_qlen); close(s); return -1; } if (connect(s, SA(&raddr), raddrlen) != 0) { mg_log(LOG_ERR, "cannot sync with peer %s, " "connect failed: %s (%d entries queued)", peer->p_name, strerror(errno), peer->p_qlen); close(s); return -1; } #endif param = O_NONBLOCK; if (fcntl(s, F_SETFL, param) != 0) { mg_log(LOG_ERR, "cannot set non blocking I/O with %s: %s", peer->p_name, strerror(errno)); } errno = 0; if ((stream = Fdopen(s, "w+")) == NULL) { mg_log(LOG_ERR, "cannot sync with peer %s, " "fdopen failed: %s (%d entries queued)", peer->p_name, (errno == 0) ? "out of stdio streams" : strerror(errno), peer->p_qlen); close(s); return -1; } #ifdef USE_FD_POOL s = fileno(stream); /* the socket descriptor could have been replaced by Fdopen() ! */ #endif if (setvbuf(stream, NULL, _IOLBF, 0) != 0) mg_log(LOG_ERR, "cannot set line buffering with peer %s: %s", peer->p_name, strerror(errno)); sync_waitdata(s, peer->p_socket_timeout); if (fgets(line, LINELEN, stream) == NULL) { mg_log(LOG_ERR, "Lost connection with peer %s: " "%s (%d entries queued)", peer->p_name, strerror(errno), peer->p_qlen); goto bad; } /* * On some systems, opening a stream on a socket introduce * weird behavior: the in and out buffers get mixed up. * By calling fflush() after each read operation, we fix that */ fflush(stream); if ((replystr = strtok_r(line, sep, &cookie)) == NULL) { mg_log(LOG_ERR, "Unexpected reply \"%s\" from peer %s " "closing connection (%d entries queued)", line, peer->p_name, peer->p_qlen); goto bad; } replycode = atoi(replystr); if (replycode != 200) { mg_log(LOG_ERR, "Unexpected reply \"%s\" from peer %s " "closing connection (%d entries queued)", line, peer->p_name, peer->p_qlen); goto bad; } if ((peer->p_vers = select_protocol(peer, s, stream)) == 0) goto bad; mg_log(LOG_INFO, "Connection to %s established, protocol version %d", peer->p_name, peer->p_vers); peer->p_stream = stream; peer->p_socket = s; return 0; bad: Fclose(stream); peer->p_stream = NULL; return -1; } void sync_master_stop(void) { if (sync_master4.sock != 1) { close(sync_master4.sock); sync_master4.sock = -1; } if (sync_master6.sock != 1) { close(sync_master6.sock); sync_master6.sock = -1; } } void sync_master_restart(void) { pthread_t tid; int empty; int error; int sync_master6_runs = SMS_NOTRUNNING; int sync_master4_runs = SMS_NOTRUNNING; PEER_RDLOCK; empty = LIST_EMPTY(&peer_head); PEER_UNLOCK; pthread_mutex_lock(&sync_master_lock); if (empty || (sync_master4.runs && sync_master6.runs)) goto last; if (conf.c_syncaddr != NULL) { if (strchr(conf.c_syncaddr, ':')) { if (sync_master6.runs != SMS_RUNNING) { sync_listen(conf.c_syncaddr, conf.c_syncport, &sync_master6); sync_master6_runs = sync_master6.runs; } sync_master4.runs = SMS_DISABLED; } else { if (sync_master4.runs != SMS_RUNNING) { sync_listen(conf.c_syncaddr, conf.c_syncport, &sync_master4); sync_master4_runs = sync_master4.runs; } sync_master6.runs = SMS_DISABLED; } } else { #ifdef AF_INET6 if (sync_master6.runs != SMS_RUNNING) { sync_listen("::", conf.c_syncport, &sync_master6); sync_master6_runs = sync_master6.runs; } #endif if (sync_master4.runs != SMS_RUNNING) { sync_listen("0.0.0.0", conf.c_syncport, &sync_master4); sync_master4_runs = sync_master4.runs; } } if (sync_master4.runs != SMS_RUNNING && sync_master6.runs != SMS_RUNNING) { mg_log(LOG_ERR, "cannot start MX sync, socket failed: %s", strerror(errno)); exit(EX_OSERR); } if (sync_master6_runs == SMS_RUNNING) { if ((error = pthread_create(&tid, NULL, sync_master, (void *)&sync_master6)) != SMS_NOTRUNNING) { mg_log(LOG_ERR, "Cannot run MX sync thread for IPv6: %s", strerror(error)); exit(EX_OSERR); } if ((error = pthread_detach(tid)) != 0) { mg_log(LOG_ERR, "pthread_detach failed for IPv6 MX sync: %s", strerror(error)); exit(EX_OSERR); } } if (sync_master4_runs == SMS_RUNNING) { if ((error = pthread_create(&tid, NULL, sync_master, (void *)&sync_master4)) != 0) { mg_log(LOG_ERR, "Cannot run MX sync thread for IPv4: %s", strerror(error)); exit(EX_OSERR); } if ((error = pthread_detach(tid)) != 0) { mg_log(LOG_ERR, "pthread_detach failed for IPv4 MX sync: %s", strerror(error)); exit(EX_OSERR); } } last: pthread_mutex_unlock(&sync_master_lock); } void * sync_master(arg) void *arg; { struct sync_master_sock *sms = arg; conf_retain(); for (;;) { sockaddr_t raddr; socklen_t raddrlen; int fd; FILE *stream; pthread_t tid; struct peer *peer; char peerstr[IPADDRSTRLEN]; int error; int sock; pthread_mutex_lock(&sync_master_lock); sock = sms->sock; pthread_mutex_unlock(&sync_master_lock); /* TODO: accept connections in nonblocking mode * in order to watch conf change */ bzero((void *)&raddr, sizeof(raddr)); raddrlen = sizeof(raddr); if ((fd = accept(sock, SA(&raddr), &raddrlen)) == -1) { mg_log(LOG_ERR, "incoming connection " "failed: %s", strerror(errno)); if (is_fatal(errno)) exit(EX_OSERR); } unmappedaddr(SA(&raddr), &raddrlen); conf_release(); conf_retain(); iptostring(SA(&raddr), raddrlen, peerstr, sizeof(peerstr)); mg_log(LOG_INFO, "Incoming MX sync connection from %s", peerstr); errno = 0; if ((stream = Fdopen(fd, "w+")) == NULL) { mg_log(LOG_ERR, "incoming connection from %s failed, " "fdopen fail: %s", peerstr, (errno == 0) ? "out of stdio streams" : strerror(errno)); close(fd); exit(EX_OSERR); } if (setvbuf(stream, NULL, _IOLBF, 0) != 0) mg_log(LOG_ERR, "cannot set line buffering: %s", strerror(errno)); /* * Check that the orginator IP is one of our peers */ PEER_RDLOCK; if (LIST_EMPTY(&peer_head)) { fprintf(stream, "105 No more peers, shutting down!\n"); PEER_UNLOCK; Fclose(stream); pthread_mutex_lock(&sync_master_lock); close(sms->sock); sms->sock = -1; sms->runs = SMS_NOTRUNNING; pthread_mutex_unlock(&sync_master_lock); conf_release(); return NULL; } LIST_FOREACH(peer, &peer_head, p_list) { #ifdef HAVE_GETADDRINFO struct addrinfo hints, *res0, *res; int err; int match = 0; bzero(&hints, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(peer->p_name, "0", &hints, &res0); if (err != 0) { mg_log(LOG_ERR, "cannot resolve %s: %s", peer->p_name, gai_strerror(err)); continue; } for (res = res0; res; res = res->ai_next) { if (ip_equal(SA(&raddr), res->ai_addr)) { match = 1; break; } } freeaddrinfo(res0); if (match) break; #else sockaddr_t addr; socklen_t addrlen; addrlen = sizeof(addr); if (ipfromstring(peer->p_name, SA(&addr), &addrlen, AF_UNSPEC) != 1) { mg_log(LOG_ERR, "cannot resolve %s", peer->p_name); continue; } if (ip_equal(SA(&raddr), SA(&addr))) break; #endif } PEER_UNLOCK; if (peer == NULL) { mg_log(LOG_INFO, "Remote host %s is not a peer MX", peerstr); fprintf(stream, "106 You have no permission to talk, go away!\n"); Fclose(stream); continue; } if ((error = pthread_create(&tid, NULL, (void *(*)(void *))sync_server, (void *)stream)) != 0) { mg_log(LOG_ERR, "incoming connection from %s failed, " "pthread_create failed: %s", peerstr, strerror(error)); Fclose(stream); continue; } if ((error = pthread_detach(tid)) != 0) { mg_log(LOG_ERR, "incoming connection from %s failed, " "pthread_detach failed: %s", peerstr, strerror(error)); exit(EX_OSERR); } } /* NOTREACHED */ mg_log(LOG_ERR, "sync_master quitted unexpectedly"); return NULL; } /* sync_master_lock must be locked */ static void sync_listen(addr, port, sms) char *addr, *port; struct sync_master_sock *sms; { struct protoent *pe; struct servent *se; int proto; sockaddr_t laddr; socklen_t laddrlen; int service; int optval; int s; sms->runs = SMS_RUNNING; laddrlen = sizeof(laddr); if (ipfromstring(addr, SA(&laddr), &laddrlen, AF_UNSPEC) != 1) { sms->runs = SMS_DISABLED; return; } if ((pe = getprotobyname("tcp")) == NULL) proto = 6; else proto = pe->p_proto; if (port != NULL) service = htons(atoi(port)); else { if ((se = getservbyname(MXGLSYNC_NAME, "tcp")) == NULL) service = htons(atoi(MXGLSYNC_PORT)); else service = se->s_port; } switch (SA(&laddr)->sa_family) { case AF_INET: SA4(&laddr)->sin_port = service; break; #ifdef AF_INET6 case AF_INET6: SA6(&laddr)->sin6_port = service; break; #endif default: sms->runs = SMS_DISABLED; return; } if ((s = socket(SA(&laddr)->sa_family, SOCK_STREAM, proto)) == -1) { sms->runs = SMS_DISABLED; return; } SET_CLOEXEC(s); optval = 1; if ((setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) != 0) { mg_log(LOG_ERR, "cannot set SO_REUSEADDR: %s", strerror(errno)); } optval = 1; if ((setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval))) != 0) { mg_log(LOG_ERR, "cannot set SO_KEEPALIVE: %s", strerror(errno)); } #ifdef IPV6_V6ONLY if (SA(&laddr)->sa_family == AF_INET6) { optval = 1; if ((setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval))) != 0) { mg_log(LOG_ERR, "cannot set IPV6_V6ONLY: %s", strerror(errno)); } } #endif if (bind(s, SA(&laddr), laddrlen) != 0) { mg_log(LOG_ERR, "cannot start MX sync, bind failed: %s", strerror(errno)); sms->runs = SMS_DISABLED; close(s); return; } if (listen(s, MXGLSYNC_BACKLOG) != 0) { mg_log(LOG_ERR, "cannot start MX sync, listen failed: %s", strerror(errno)); sms->runs = SMS_DISABLED; close(s); return; } sms->sock = s; return; } void sync_server(arg) void *arg; { FILE *stream = arg; char sep[] = " \n\t\r"; char *cmd; char *keyword; char *addrstr; char *from; char *rcpt; char from_clean[ADDRLEN + 1]; char rcpt_clean[ADDRLEN + 1]; char *datestr; char *awstr; char *cookie; char line[LINELEN + 1]; peer_sync_t action; sockaddr_t addr; socklen_t addrlen; time_t date; time_t aw; conf_retain(); aw = time(NULL) + conf.c_autowhite_validity; fprintf(stream, "200 Yeah, what do you want?\n"); fflush(stream); for (;;) { if ((fgets(line, LINELEN, stream)) == NULL) break; /* * On some systems, opening a stream on a socket introduce * weird behavior: the in and out buffers get mixed up. * By calling fflush() after each read operation, we fix that */ fflush(stream); /* * Get the command { quit | help | add | del | del2 | flush } */ cookie = NULL; if ((cmd = strtok_r(line, sep, &cookie)) == NULL) { fprintf(stream, "101 No command\n"); fflush(stream); continue; } if (strncmp(cmd, "quit", CMDLEN) == 0) { break; } else if ((strncmp(cmd, "help", CMDLEN)) == 0) { sync_help(stream); continue; } else if ((strncmp(cmd, "vers3", CMDLEN)) == 0) { sync_vers(stream, 3); continue; } else if ((strncmp(cmd, "vers2", CMDLEN)) == 0) { sync_vers(stream, 2); continue; } else if ((strncmp(cmd, "add", CMDLEN)) == 0) { action = PS_CREATE; } else if ((strncmp(cmd, "del2", CMDLEN)) == 0) { action = PS_DELETE; aw = -1; } else if ((strncmp(cmd, "del", CMDLEN)) == 0) { action = PS_DELETE; } else if ((strncmp(cmd, "flush", CMDLEN)) == 0) { action = PS_FLUSH; } else { fprintf(stream, "102 Invalid command \"%s\"\n", cmd); fflush(stream); continue; } /* * get { "addr" ip_address } */ if ((keyword = strtok_r(NULL, sep, &cookie)) == NULL) { fprintf(stream, "103 Incomplete command\n"); fflush(stream); continue; } if (strncmp(keyword, "addr", CMDLEN) != 0) { fprintf(stream, "104 Unexpected keyword \"%s\"\n", keyword); fflush(stream); continue; } if ((addrstr = strtok_r(NULL, sep, &cookie)) == NULL) { fprintf(stream, "103 Incomplete command\n"); fflush(stream); continue; } addrlen = sizeof(addr); if (ipfromstring(addrstr, SA(&addr), &addrlen, AF_UNSPEC) != 1) { fprintf(stream, "107 Invalid IP address\n"); fflush(stream); continue; } if (action == PS_FLUSH) { from = NULL; rcpt = NULL; date = 0; goto eol; } /* * get { "from" email_address } */ if ((keyword = strtok_r(NULL, sep, &cookie)) == NULL) { fprintf(stream, "103 Incomplete command\n"); fflush(stream); continue; } if (strncmp(keyword, "from", CMDLEN) != 0) { fprintf(stream, "104 Unexpected keyword \"%s\"\n", keyword); fflush(stream); continue; } if ((from = strtok_r(NULL, sep, &cookie)) == NULL) { fprintf(stream, "103 Incomplete command\n"); fflush(stream); continue; } (void)strncpy_rmsp(from_clean, from, ADDRLEN); from = from_clean; /* * get { "rcpt" email_address } */ if ((keyword = strtok_r(NULL, sep, &cookie)) == NULL) { fprintf(stream, "103 Incomplete command\n"); fflush(stream); continue; } if (strncmp(keyword, "rcpt", CMDLEN) != 0) { fprintf(stream, "104 Unexpected keyword \"%s\"\n", keyword); fflush(stream); continue; } if ((rcpt = strtok_r(NULL, sep, &cookie)) == NULL) { fprintf(stream, "103 Incomplete command\n"); fflush(stream); continue; } (void)strncpy_rmsp(rcpt_clean, rcpt, ADDRLEN); rcpt = rcpt_clean; /* * get { "date" valid_date } */ if ((keyword = strtok_r(NULL, sep, &cookie)) == NULL) { fprintf(stream, "103 Incomplete command\n"); fflush(stream); continue; } if (strncmp(keyword, "date", CMDLEN) != 0) { fprintf(stream, "104 Unexpected keyword \"%s\"\n", keyword); fflush(stream); continue; } if ((datestr = strtok_r(NULL, sep, &cookie)) == NULL) { fprintf(stream, "103 Incomplete command\n"); fflush(stream); continue; } date = atoi(datestr); if (aw == -1) { /* * get { "aw" valid_date } */ if ((keyword = strtok_r(NULL, sep, &cookie)) == NULL) { fprintf(stream, "103 Incomplete command\n"); fflush(stream); continue; } if (strncmp(keyword, "aw", CMDLEN) != 0) { fprintf(stream, "104 Unexpected keyword \"%s\"\n", keyword); fflush(stream); continue; } if ((awstr = strtok_r(NULL, sep, &cookie)) == NULL) { fprintf(stream, "103 Incomplete command\n"); fflush(stream); continue; } aw = atoi(awstr); } /* * Check nothing remains */ eol: if ((keyword = strtok_r(NULL, sep, &cookie)) != NULL) { fprintf(stream, "104 Unexpected keyword \"%s\"\n", keyword); fflush(stream); continue; } fprintf(stream, "201 All right, I'll do that\n"); fflush(stream); if (action == PS_CREATE) { int dirty = 0; PENDING_LOCK; /* delay = -1 means unused: we supply the date */ if (pending_get(SA(&addr), addrlen, from, rcpt, date, 0, T_PENDING)) ++dirty; PENDING_UNLOCK; dump_touch(dirty); } if (action == PS_DELETE) { pending_del(SA(&addr), addrlen, from, rcpt, date, aw); } if (action == PS_FLUSH) { pending_del_addr(SA(&addr), addrlen, NULL, 0); } /* Flush modifications to disk */ dump_flush(); } fprintf(stream, "202 Good bye\n"); Fclose(stream); conf_release(); return; } static void sync_vers(stream, vers) FILE *stream; int vers; { if (vers <= SYNC_PROTO_CURRENT) { fprintf(stream, "%d Yes, I speak version %d, what do you think?\n", 800 + vers, vers); } else { fprintf(stream, "108 Invalid vers%d command\n", vers); } fflush(stream); return; } void sync_help(stream) FILE *stream; { fprintf(stream, "203 Help? Sure, we have help here:\n"); fprintf(stream, "203 \n"); fprintf(stream, "203 Available commands are:\n"); fprintf(stream, "203 help -- displays this message\n"); fprintf(stream, "203 quit -- terminate connection\n"); fprintf(stream, "203 vers2 -- speak version 2 protocol\n"); fprintf(stream, "203 vers3 -- speak version 3 protocol\n"); fprintf(stream, "203 add addr from rcpt date