1 /*
2  * ProFTPD - mod_snmp database storage
3  * Copyright (c) 2008-2017 TJ Saunders
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, TJ Saunders and other respective copyright holders
20  * give permission to link this program with OpenSSL, and distribute the
21  * resulting executable, without including the source code for OpenSSL in the
22  * source distribution.
23  */
24 
25 #include "mod_snmp.h"
26 #include "db.h"
27 #include "uptime.h"
28 
29 /* On some platforms, this may not be defined.  On AIX, for example, this
30  * symbol is only defined when _NO_PROTO is defined, and _XOPEN_SOURCE is 500.
31  * How annoying.
32  */
33 #ifndef MAP_FAILED
34 # define MAP_FAILED	((void *) -1)
35 #endif
36 
37 #define SNMP_MAX_LOCK_ATTEMPTS		10
38 
39 /* Note: Not all database IDs are in this list; only those databases which
40  * have on-disk tables are here.  Thus the NOTIFY and CONN database IDs are
41  * explicitly NOT here, as they are ephemeral/synthetic databases anyway.
42  */
43 int snmp_table_ids[] = {
44   SNMP_DB_ID_DAEMON,
45   SNMP_DB_ID_TIMEOUTS,
46   SNMP_DB_ID_FTP,
47   SNMP_DB_ID_SNMP,
48   SNMP_DB_ID_TLS,
49   SNMP_DB_ID_SSH,
50   SNMP_DB_ID_SFTP,
51   SNMP_DB_ID_SCP,
52   SNMP_DB_ID_BAN,
53 
54   /* XXX Not supported just yet */
55 #if 0
56   SNMP_DB_ID_SQL,
57   SNMP_DB_ID_QUOTA,
58   SNMP_DB_ID_GEOIP,
59 #endif
60 
61   SNMP_DB_ID_UNKNOWN
62 };
63 
64 static const char *snmp_db_root = NULL;
65 
66 static const char *trace_channel = "snmp.db";
67 
68 struct snmp_field_info {
69   unsigned int field;
70   int db_id;
71   off_t field_start;
72   size_t field_len;
73   const char *field_name;
74 };
75 
76 static struct snmp_field_info snmp_fields[] = {
77 
78   /* Miscellaneous SNMP-related fields */
79   { SNMP_DB_NOTIFY_F_SYS_UPTIME, SNMP_DB_ID_NOTIFY, 0,
80     0, "NOTIFY_F_SYS_UPTIME" },
81 
82   /* Connection fields */
83   { SNMP_DB_CONN_F_SERVER_NAME, SNMP_DB_ID_CONN, 0,
84     0, "CONN_F_SERVER_NAME" },
85   { SNMP_DB_CONN_F_SERVER_ADDR, SNMP_DB_ID_CONN, 0,
86     0, "CONN_F_SERVER_ADDR" },
87   { SNMP_DB_CONN_F_SERVER_PORT, SNMP_DB_ID_CONN, 0,
88     0, "CONN_F_SERVER_PORT" },
89   { SNMP_DB_CONN_F_CLIENT_ADDR, SNMP_DB_ID_CONN, 0,
90     0, "CONN_F_CLIENT_ADDR" },
91   { SNMP_DB_CONN_F_PID, SNMP_DB_ID_CONN, 0,
92     0, "CONN_F_PID" },
93   { SNMP_DB_CONN_F_USER_NAME, SNMP_DB_ID_CONN, 0,
94     0, "CONN_F_USER_NAME" },
95   { SNMP_DB_CONN_F_PROTOCOL, SNMP_DB_ID_CONN, 0,
96     0, "CONN_F_PROTOCOL" },
97 
98   /* Daemon fields */
99   { SNMP_DB_DAEMON_F_SOFTWARE, SNMP_DB_ID_DAEMON, 0,
100     0, "DAEMON_F_SOFTWARE" },
101   { SNMP_DB_DAEMON_F_VERSION, SNMP_DB_ID_DAEMON, 0,
102     0, "DAEMON_F_VERSION" },
103   { SNMP_DB_DAEMON_F_ADMIN, SNMP_DB_ID_DAEMON, 0,
104     0, "DAEMON_F_ADMIN" },
105   { SNMP_DB_DAEMON_F_UPTIME, SNMP_DB_ID_DAEMON, 0,
106     0, "DAEMON_F_UPTIME" },
107   { SNMP_DB_DAEMON_F_VHOST_COUNT, SNMP_DB_ID_DAEMON, 0,
108     sizeof(uint32_t), "DAEMON_F_VHOST_COUNT" },
109   { SNMP_DB_DAEMON_F_CONN_COUNT, SNMP_DB_ID_DAEMON, 4,
110     sizeof(uint32_t), "DAEMON_F_CONN_COUNT" },
111   { SNMP_DB_DAEMON_F_CONN_TOTAL, SNMP_DB_ID_DAEMON, 8,
112     sizeof(uint32_t), "DAEMON_F_CONN_TOTAL" },
113   { SNMP_DB_DAEMON_F_CONN_REFUSED_TOTAL, SNMP_DB_ID_DAEMON, 12,
114     sizeof(uint32_t), "DAEMON_F_CONN_REFUSED_TOTAL" },
115   { SNMP_DB_DAEMON_F_RESTART_COUNT, SNMP_DB_ID_DAEMON, 16,
116     sizeof(uint32_t), "DAEMON_F_RESTART_COUNT" },
117   { SNMP_DB_DAEMON_F_SEGFAULT_COUNT, SNMP_DB_ID_DAEMON, 20,
118     sizeof(uint32_t), "DAEMON_F_SEGFAULT_COUNT" },
119   { SNMP_DB_DAEMON_F_MAXINST_TOTAL, SNMP_DB_ID_DAEMON, 24,
120     sizeof(uint32_t), "DAEMON_F_MAXINST_TOTAL" },
121   { SNMP_DB_DAEMON_F_MAXINST_CONF, SNMP_DB_ID_DAEMON, 28,
122     sizeof(uint32_t), "DAEMON_F_MAXINST_CONF" },
123 
124   /* timeouts fields */
125   { SNMP_DB_TIMEOUTS_F_IDLE_TOTAL, SNMP_DB_ID_TIMEOUTS, 0,
126     sizeof(uint32_t), "TIMEOUTS_F_IDLE_TOTAL" },
127   { SNMP_DB_TIMEOUTS_F_LOGIN_TOTAL, SNMP_DB_ID_TIMEOUTS, 4,
128     sizeof(uint32_t), "TIMEOUTS_F_LOGIN_TOTAL" },
129   { SNMP_DB_TIMEOUTS_F_NOXFER_TOTAL, SNMP_DB_ID_TIMEOUTS, 8,
130     sizeof(uint32_t), "TIMEOUTS_F_NOXFER_TOTAL" },
131   { SNMP_DB_TIMEOUTS_F_STALLED_TOTAL, SNMP_DB_ID_TIMEOUTS, 12,
132     sizeof(uint32_t), "TIMEOUTS_F_STALLED_TOTAL" },
133 
134   /* ftp.sessions fields */
135   { SNMP_DB_FTP_SESS_F_SESS_COUNT, SNMP_DB_ID_FTP, 0,
136     sizeof(uint32_t), "FTP_SESS_F_SESS_COUNT" },
137   { SNMP_DB_FTP_SESS_F_SESS_TOTAL, SNMP_DB_ID_FTP, 4,
138     sizeof(uint32_t), "FTP_SESS_F_SESS_TOTAL" },
139   { SNMP_DB_FTP_SESS_F_CMD_INVALID_TOTAL, SNMP_DB_ID_FTP, 8,
140     sizeof(uint32_t), "FTP_SESS_F_CMD_INVALID_TOTAL" },
141 
142   /* ftp.logins fields */
143   { SNMP_DB_FTP_LOGINS_F_TOTAL, SNMP_DB_ID_FTP, 12,
144     sizeof(uint32_t), "FTP_LOGINS_F_TOTAL" },
145   { SNMP_DB_FTP_LOGINS_F_ERR_TOTAL, SNMP_DB_ID_FTP, 16,
146     sizeof(uint32_t), "FTP_LOGINS_F_ERR_TOTAL" },
147   { SNMP_DB_FTP_LOGINS_F_ERR_BAD_USER_TOTAL, SNMP_DB_ID_FTP, 20,
148     sizeof(uint32_t), "FTP_LOGINS_F_ERR_BAD_USER_TOTAL" },
149   { SNMP_DB_FTP_LOGINS_F_ERR_BAD_PASSWD_TOTAL, SNMP_DB_ID_FTP, 24,
150     sizeof(uint32_t), "FTP_LOGINS_F_ERR_BAD_PASSWD_TOTAL" },
151   { SNMP_DB_FTP_LOGINS_F_ERR_GENERAL_TOTAL, SNMP_DB_ID_FTP, 28,
152     sizeof(uint32_t), "FTP_LOGINS_F_ERR_GENERAL_TOTAL" },
153   { SNMP_DB_FTP_LOGINS_F_ANON_COUNT, SNMP_DB_ID_FTP, 32,
154     sizeof(uint32_t), "FTP_LOGINS_F_ANON_COUNT" },
155   { SNMP_DB_FTP_LOGINS_F_ANON_TOTAL, SNMP_DB_ID_FTP, 36,
156     sizeof(uint32_t), "FTP_LOGINS_F_ANON_TOTAL" },
157 
158   /* ftp.dataTransfers fields */
159   { SNMP_DB_FTP_XFERS_F_DIR_LIST_COUNT, SNMP_DB_ID_FTP, 40,
160     sizeof(uint32_t), "FTP_XFERS_F_DIR_LIST_COUNT" },
161   { SNMP_DB_FTP_XFERS_F_DIR_LIST_TOTAL, SNMP_DB_ID_FTP, 44,
162     sizeof(uint32_t), "FTP_XFERS_F_DIR_LIST_TOTAL" },
163   { SNMP_DB_FTP_XFERS_F_DIR_LIST_ERR_TOTAL, SNMP_DB_ID_FTP, 48,
164     sizeof(uint32_t), "FTP_XFERS_F_DIR_LIST_ERR_TOTAL" },
165   { SNMP_DB_FTP_XFERS_F_FILE_UPLOAD_COUNT, SNMP_DB_ID_FTP, 52,
166     sizeof(uint32_t), "FTP_XFERS_F_FILE_UPLOAD_COUNT" },
167   { SNMP_DB_FTP_XFERS_F_FILE_UPLOAD_TOTAL, SNMP_DB_ID_FTP, 56,
168     sizeof(uint32_t), "FTP_XFERS_F_FILE_UPLOAD_TOTAL" },
169   { SNMP_DB_FTP_XFERS_F_FILE_UPLOAD_ERR_TOTAL, SNMP_DB_ID_FTP, 60,
170     sizeof(uint32_t), "FTP_XFERS_F_FILE_UPLOAD_ERR_TOTAL" },
171   { SNMP_DB_FTP_XFERS_F_FILE_DOWNLOAD_COUNT, SNMP_DB_ID_FTP, 64,
172     sizeof(uint32_t), "FTP_XFERS_F_FILE_DOWNLOAD_COUNT" },
173   { SNMP_DB_FTP_XFERS_F_FILE_DOWNLOAD_TOTAL, SNMP_DB_ID_FTP, 68,
174     sizeof(uint32_t), "FTP_XFERS_F_FILE_DOWNLOAD_TOTAL" },
175   { SNMP_DB_FTP_XFERS_F_FILE_DOWNLOAD_ERR_TOTAL, SNMP_DB_ID_FTP, 72,
176     sizeof(uint32_t), "FTP_XFERS_F_FILE_DOWNLOAD_ERR_TOTAL" },
177   { SNMP_DB_FTP_XFERS_F_KB_UPLOAD_TOTAL, SNMP_DB_ID_FTP, 76,
178     sizeof(uint32_t), "FTP_XFERS_F_KB_UPLOAD_TOTAL" },
179   { SNMP_DB_FTP_XFERS_F_KB_DOWNLOAD_TOTAL, SNMP_DB_ID_FTP, 80,
180     sizeof(uint32_t), "FTP_XFERS_F_KB_DOWNLOAD_TOTAL" },
181 
182   /* snmp fields */
183   { SNMP_DB_SNMP_F_PKTS_RECVD_TOTAL, SNMP_DB_ID_SNMP, 0,
184     sizeof(uint32_t), "SNMP_F_PKTS_RECVD_TOTAL" },
185   { SNMP_DB_SNMP_F_PKTS_SENT_TOTAL, SNMP_DB_ID_SNMP, 4,
186     sizeof(uint32_t), "SNMP_F_PKTS_SENT_TOTAL" },
187   { SNMP_DB_SNMP_F_TRAPS_SENT_TOTAL, SNMP_DB_ID_SNMP, 8,
188     sizeof(uint32_t), "SNMP_F_TRAPS_SENT_TOTAL" },
189   { SNMP_DB_SNMP_F_PKTS_AUTH_ERR_TOTAL, SNMP_DB_ID_SNMP, 12,
190     sizeof(uint32_t), "SNMP_F_PKTS_AUTH_ERR_TOTAL" },
191   { SNMP_DB_SNMP_F_PKTS_DROPPED_TOTAL, SNMP_DB_ID_SNMP, 16,
192     sizeof(uint32_t), "SNMP_F_PKTS_DROPPED_TOTAL" },
193 
194   /* ftps.tlsSessions fields */
195   { SNMP_DB_FTPS_SESS_F_SESS_COUNT, SNMP_DB_ID_TLS, 0,
196     sizeof(uint32_t), "FTPS_SESS_F_SESS_COUNT" },
197   { SNMP_DB_FTPS_SESS_F_SESS_TOTAL, SNMP_DB_ID_TLS, 4,
198     sizeof(uint32_t), "FTPS_SESS_F_SESS_TOTAL" },
199   { SNMP_DB_FTPS_SESS_F_CTRL_HANDSHAKE_ERR_TOTAL, SNMP_DB_ID_TLS, 8,
200     sizeof(uint32_t), "FTPS_SESS_F_CTRL_HANDSHAKE_ERR_TOTAL" },
201   { SNMP_DB_FTPS_SESS_F_DATA_HANDSHAKE_ERR_TOTAL, SNMP_DB_ID_TLS, 12,
202     sizeof(uint32_t), "FTPS_SESS_F_DATA_HANDSHAKE_ERR_TOTAL" },
203   { SNMP_DB_FTPS_SESS_F_CCC_TOTAL, SNMP_DB_ID_TLS, 16,
204     sizeof(uint32_t), "FTPS_SESS_F_CCC_TOTAL" },
205   { SNMP_DB_FTPS_SESS_F_CCC_ERR_TOTAL, SNMP_DB_ID_TLS, 20,
206     sizeof(uint32_t), "FTPS_SESS_F_CCC_ERR_TOTAL" },
207   { SNMP_DB_FTPS_SESS_F_VERIFY_CLIENT_TOTAL, SNMP_DB_ID_TLS, 24,
208     sizeof(uint32_t), "FTPS_SESS_F_VERIFY_CLIENT_TOTAL" },
209   { SNMP_DB_FTPS_SESS_F_VERIFY_CLIENT_ERR_TOTAL, SNMP_DB_ID_TLS, 28,
210     sizeof(uint32_t), "FTPS_SESS_F_VERIFY_CLIENT_ERR_TOTAL" },
211 
212   /* ftps.tlsLogins fields */
213   { SNMP_DB_FTPS_LOGINS_F_TOTAL, SNMP_DB_ID_TLS, 32,
214     sizeof(uint32_t), "FTPS_LOGINS_F_TOTAL" },
215   { SNMP_DB_FTPS_LOGINS_F_ERR_TOTAL, SNMP_DB_ID_TLS, 36,
216     sizeof(uint32_t), "FTPS_LOGINS_F_ERR_TOTAL" },
217   { SNMP_DB_FTPS_LOGINS_F_ERR_BAD_USER_TOTAL, SNMP_DB_ID_TLS, 40,
218     sizeof(uint32_t), "FTPS_LOGINS_F_ERR_BAD_USER_TOTAL" },
219   { SNMP_DB_FTPS_LOGINS_F_ERR_BAD_PASSWD_TOTAL, SNMP_DB_ID_TLS, 44,
220     sizeof(uint32_t), "FTPS_LOGINS_F_ERR_BAD_PASSWD_TOTAL" },
221   { SNMP_DB_FTPS_LOGINS_F_ERR_GENERAL_TOTAL, SNMP_DB_ID_TLS, 48,
222     sizeof(uint32_t), "FTPS_LOGINS_F_ERR_GENERAL_TOTAL" },
223   { SNMP_DB_FTPS_LOGINS_F_CERT_TOTAL, SNMP_DB_ID_TLS, 52,
224     sizeof(uint32_t), "FTPS_LOGINS_F_CERT_TOTAL" },
225 
226   /* ftps.tlsDataTransfers fields */
227   { SNMP_DB_FTPS_XFERS_F_DIR_LIST_COUNT, SNMP_DB_ID_TLS, 56,
228     sizeof(uint32_t), "FTPS_XFERS_F_DIR_LIST_COUNT" },
229   { SNMP_DB_FTPS_XFERS_F_DIR_LIST_TOTAL, SNMP_DB_ID_TLS, 60,
230     sizeof(uint32_t), "FTPS_XFERS_F_DIR_LIST_TOTAL" },
231   { SNMP_DB_FTPS_XFERS_F_DIR_LIST_ERR_TOTAL, SNMP_DB_ID_TLS, 64,
232     sizeof(uint32_t), "FTPS_XFERS_F_DIR_LIST_ERR_TOTAL" },
233   { SNMP_DB_FTPS_XFERS_F_FILE_UPLOAD_COUNT, SNMP_DB_ID_TLS, 68,
234     sizeof(uint32_t), "FTPS_XFERS_F_FILE_UPLOAD_COUNT" },
235   { SNMP_DB_FTPS_XFERS_F_FILE_UPLOAD_TOTAL, SNMP_DB_ID_TLS, 72,
236     sizeof(uint32_t), "FTPS_XFERS_F_FILE_UPLOAD_TOTAL" },
237   { SNMP_DB_FTPS_XFERS_F_FILE_UPLOAD_ERR_TOTAL, SNMP_DB_ID_TLS, 76,
238     sizeof(uint32_t), "FTPS_XFERS_F_FILE_UPLOAD_ERR_TOTAL" },
239   { SNMP_DB_FTPS_XFERS_F_FILE_DOWNLOAD_COUNT, SNMP_DB_ID_TLS, 80,
240     sizeof(uint32_t), "FTPS_XFERS_F_FILE_DOWNLOAD_COUNT" },
241   { SNMP_DB_FTPS_XFERS_F_FILE_DOWNLOAD_TOTAL, SNMP_DB_ID_TLS, 84,
242     sizeof(uint32_t), "FTPS_XFERS_F_FILE_DOWNLOAD_TOTAL" },
243   { SNMP_DB_FTPS_XFERS_F_FILE_DOWNLOAD_ERR_TOTAL, SNMP_DB_ID_TLS, 88,
244     sizeof(uint32_t), "FTPS_XFERS_F_FILE_DOWNLOAD_ERR_TOTAL" },
245   { SNMP_DB_FTPS_XFERS_F_KB_UPLOAD_TOTAL, SNMP_DB_ID_TLS, 92,
246     sizeof(uint32_t), "FTPS_XFERS_F_KB_UPLOAD_TOTAL" },
247   { SNMP_DB_FTPS_XFERS_F_KB_DOWNLOAD_TOTAL, SNMP_DB_ID_TLS, 96,
248     sizeof(uint32_t), "FTPS_XFERS_F_KB_DOWNLOAD_TOTAL" },
249 
250   /* ssh.sshSessions fields */
251   { SNMP_DB_SSH_SESS_F_KEX_ERR_TOTAL, SNMP_DB_ID_SSH, 0,
252     sizeof(uint32_t), "SSH_SESS_F_KEX_ERR_TOTAL" },
253   { SNMP_DB_SSH_SESS_F_C2S_COMPRESS_TOTAL, SNMP_DB_ID_SSH, 4,
254     sizeof(uint32_t), "SSH_SESS_F_C2S_COMPRESS_TOTAL" },
255   { SNMP_DB_SSH_SESS_F_S2C_COMPRESS_TOTAL, SNMP_DB_ID_SSH, 8,
256     sizeof(uint32_t), "SSH_SESS_F_S2C_COMPRESS_TOTAL" },
257 
258   /* ssh.sshLogins fields */
259   { SNMP_DB_SSH_LOGINS_F_HOSTBASED_TOTAL, SNMP_DB_ID_SSH, 12,
260     sizeof(uint32_t), "SSH_LOGINS_F_HOSTBASED_TOTAL" },
261   { SNMP_DB_SSH_LOGINS_F_HOSTBASED_ERR_TOTAL, SNMP_DB_ID_SSH, 16,
262     sizeof(uint32_t), "SSH_LOGINS_F_HOSTBASED_ERR_TOTAL" },
263   { SNMP_DB_SSH_LOGINS_F_KBDINT_TOTAL, SNMP_DB_ID_SSH, 20,
264     sizeof(uint32_t), "SSH_LOGINS_F_KBDINT_TOTAL" },
265   { SNMP_DB_SSH_LOGINS_F_KBDINT_ERR_TOTAL, SNMP_DB_ID_SSH, 24,
266     sizeof(uint32_t), "SSH_LOGINS_F_KBDINT_ERR_TOTAL" },
267   { SNMP_DB_SSH_LOGINS_F_PASSWD_TOTAL, SNMP_DB_ID_SSH, 28,
268     sizeof(uint32_t), "SSH_LOGINS_F_PASSWD_TOTAL" },
269   { SNMP_DB_SSH_LOGINS_F_PASSWD_ERR_TOTAL, SNMP_DB_ID_SSH, 32,
270     sizeof(uint32_t), "SSH_LOGINS_F_PASSWD_ERR_TOTAL" },
271   { SNMP_DB_SSH_LOGINS_F_PUBLICKEY_TOTAL, SNMP_DB_ID_SSH, 36,
272     sizeof(uint32_t), "SSH_LOGINS_F_PUBLICKEY_TOTAL" },
273   { SNMP_DB_SSH_LOGINS_F_PUBLICKEY_ERR_TOTAL, SNMP_DB_ID_SSH, 40,
274     sizeof(uint32_t), "SSH_LOGINS_F_PUBLICKEY_ERR_TOTAL" },
275 
276   /* sftp.sftpSessions fields */
277   { SNMP_DB_SFTP_SESS_F_SESS_COUNT, SNMP_DB_ID_SFTP, 0,
278     sizeof(uint32_t), "SFTP_SESS_F_SESS_COUNT" },
279   { SNMP_DB_SFTP_SESS_F_SESS_TOTAL, SNMP_DB_ID_SFTP, 4,
280     sizeof(uint32_t), "SFTP_SESS_F_SESS_TOTAL" },
281   { SNMP_DB_SFTP_SESS_F_SFTP_V3_TOTAL, SNMP_DB_ID_SFTP, 8,
282     sizeof(uint32_t), "SFTP_SESS_F_SFTP_V3_TOTAL" },
283   { SNMP_DB_SFTP_SESS_F_SFTP_V4_TOTAL, SNMP_DB_ID_SFTP, 12,
284     sizeof(uint32_t), "SFTP_SESS_F_SFTP_V4_TOTAL" },
285   { SNMP_DB_SFTP_SESS_F_SFTP_V5_TOTAL, SNMP_DB_ID_SFTP, 16,
286     sizeof(uint32_t), "SFTP_SESS_F_SFTP_V5_TOTAL" },
287   { SNMP_DB_SFTP_SESS_F_SFTP_V6_TOTAL, SNMP_DB_ID_SFTP, 20,
288     sizeof(uint32_t), "SFTP_SESS_F_SFTP_V6_TOTAL" },
289 
290   /* sftp.sftpDataTransfers fields */
291   { SNMP_DB_SFTP_XFERS_F_DIR_LIST_COUNT, SNMP_DB_ID_SFTP, 24,
292     sizeof(uint32_t), "SFTP_XFERS_F_DIR_LIST_COUNT" },
293   { SNMP_DB_SFTP_XFERS_F_DIR_LIST_TOTAL, SNMP_DB_ID_SFTP, 28,
294     sizeof(uint32_t), "SFTP_XFERS_F_DIR_LIST_TOTAL" },
295   { SNMP_DB_SFTP_XFERS_F_DIR_LIST_ERR_TOTAL, SNMP_DB_ID_SFTP, 32,
296     sizeof(uint32_t), "SFTP_XFERS_F_DIR_LIST_ERR_TOTAL" },
297   { SNMP_DB_SFTP_XFERS_F_FILE_UPLOAD_COUNT, SNMP_DB_ID_SFTP, 36,
298     sizeof(uint32_t), "SFTP_XFERS_F_FILE_UPLOAD_COUNT" },
299   { SNMP_DB_SFTP_XFERS_F_FILE_UPLOAD_TOTAL, SNMP_DB_ID_SFTP, 40,
300     sizeof(uint32_t), "SFTP_XFERS_F_FILE_UPLOAD_TOTAL" },
301   { SNMP_DB_SFTP_XFERS_F_FILE_UPLOAD_ERR_TOTAL, SNMP_DB_ID_SFTP, 44,
302     sizeof(uint32_t), "SFTP_XFERS_F_FILE_UPLOAD_ERR_TOTAL" },
303   { SNMP_DB_SFTP_XFERS_F_FILE_DOWNLOAD_COUNT, SNMP_DB_ID_SFTP, 48,
304     sizeof(uint32_t), "SFTP_XFERS_F_FILE_DOWNLOAD_COUNT" },
305   { SNMP_DB_SFTP_XFERS_F_FILE_DOWNLOAD_TOTAL, SNMP_DB_ID_SFTP, 52,
306     sizeof(uint32_t), "SFTP_XFERS_F_FILE_DOWNLOAD_TOTAL" },
307   { SNMP_DB_SFTP_XFERS_F_FILE_DOWNLOAD_ERR_TOTAL, SNMP_DB_ID_SFTP, 56,
308     sizeof(uint32_t), "SFTP_XFERS_F_FILE_DOWNLOAD_ERR_TOTAL" },
309   { SNMP_DB_SFTP_XFERS_F_KB_UPLOAD_TOTAL, SNMP_DB_ID_SFTP, 60,
310     sizeof(uint32_t), "SFTP_XFERS_F_KB_UPLOAD_TOTAL" },
311   { SNMP_DB_SFTP_XFERS_F_KB_DOWNLOAD_TOTAL, SNMP_DB_ID_SFTP, 64,
312     sizeof(uint32_t), "SFTP_XFERS_F_KB_DOWNLOAD_TOTAL" },
313 
314   /* scp.scpSessions fields */
315   { SNMP_DB_SCP_SESS_F_SESS_COUNT, SNMP_DB_ID_SCP, 0,
316     sizeof(uint32_t), "SCP_SESS_F_SESS_COUNT" },
317   { SNMP_DB_SCP_SESS_F_SESS_TOTAL, SNMP_DB_ID_SCP, 4,
318     sizeof(uint32_t), "SCP_SESS_F_SESS_TOTAL" },
319 
320   /* scp.scpDataTransfers fields */
321   { SNMP_DB_SCP_XFERS_F_FILE_UPLOAD_COUNT, SNMP_DB_ID_SCP, 8,
322     sizeof(uint32_t), "SCP_XFERS_F_FILE_UPLOAD_COUNT" },
323   { SNMP_DB_SCP_XFERS_F_FILE_UPLOAD_TOTAL, SNMP_DB_ID_SCP, 12,
324     sizeof(uint32_t), "SCP_XFERS_F_FILE_UPLOAD_TOTAL" },
325   { SNMP_DB_SCP_XFERS_F_FILE_UPLOAD_ERR_TOTAL, SNMP_DB_ID_SCP, 16,
326     sizeof(uint32_t), "SCP_XFERS_F_FILE_UPLOAD_ERR_TOTAL" },
327   { SNMP_DB_SCP_XFERS_F_FILE_DOWNLOAD_COUNT, SNMP_DB_ID_SCP, 20,
328     sizeof(uint32_t), "SCP_XFERS_F_FILE_DOWNLOAD_COUNT" },
329   { SNMP_DB_SCP_XFERS_F_FILE_DOWNLOAD_TOTAL, SNMP_DB_ID_SCP, 24,
330     sizeof(uint32_t), "SCP_XFERS_F_FILE_DOWNLOAD_TOTAL" },
331   { SNMP_DB_SCP_XFERS_F_FILE_DOWNLOAD_ERR_TOTAL, SNMP_DB_ID_SCP, 28,
332     sizeof(uint32_t), "SCP_XFERS_F_FILE_DOWNLOAD_ERR_TOTAL" },
333   { SNMP_DB_SCP_XFERS_F_KB_UPLOAD_TOTAL, SNMP_DB_ID_SCP, 32,
334     sizeof(uint32_t), "SCP_XFERS_F_KB_UPLOAD_TOTAL" },
335   { SNMP_DB_SCP_XFERS_F_KB_DOWNLOAD_TOTAL, SNMP_DB_ID_SCP, 36,
336     sizeof(uint32_t), "SCP_XFERS_F_KB_DOWNLOAD_TOTAL" },
337 
338   /* ban.connections fields */
339   { SNMP_DB_BAN_CONNS_F_CONN_BAN_TOTAL, SNMP_DB_ID_BAN, 0,
340     sizeof(uint32_t), "BAN_CONNS_F_CONN_BAN_TOTAL" },
341   { SNMP_DB_BAN_CONNS_F_USER_BAN_TOTAL, SNMP_DB_ID_BAN, 4,
342     sizeof(uint32_t), "BAN_CONNS_F_USER_BAN_TOTAL" },
343   { SNMP_DB_BAN_CONNS_F_HOST_BAN_TOTAL, SNMP_DB_ID_BAN, 8,
344     sizeof(uint32_t), "BAN_CONNS_F_HOST_BAN_TOTAL" },
345   { SNMP_DB_BAN_CONNS_F_CLASS_BAN_TOTAL, SNMP_DB_ID_BAN, 12,
346     sizeof(uint32_t), "BAN_CONNS_F_CLASS_BAN_TOTAL" },
347 
348   /* ban.bans fields */
349   { SNMP_DB_BAN_BANS_F_BAN_COUNT, SNMP_DB_ID_BAN, 16,
350     sizeof(uint32_t), "BAN_BANS_F_BAN_COUNT" },
351   { SNMP_DB_BAN_BANS_F_BAN_TOTAL, SNMP_DB_ID_BAN, 20,
352     sizeof(uint32_t), "BAN_BANS_F_BAN_TOTAL" },
353   { SNMP_DB_BAN_BANS_F_USER_BAN_COUNT, SNMP_DB_ID_BAN, 24,
354     sizeof(uint32_t), "BAN_BANS_F_USER_BAN_COUNT" },
355   { SNMP_DB_BAN_BANS_F_USER_BAN_TOTAL, SNMP_DB_ID_BAN, 28,
356     sizeof(uint32_t), "BAN_BANS_F_USER_BAN_TOTAL" },
357   { SNMP_DB_BAN_BANS_F_HOST_BAN_COUNT, SNMP_DB_ID_BAN, 32,
358     sizeof(uint32_t), "BAN_BANS_F_HOST_BAN_COUNT" },
359   { SNMP_DB_BAN_BANS_F_HOST_BAN_TOTAL, SNMP_DB_ID_BAN, 36,
360     sizeof(uint32_t), "BAN_BANS_F_HOST_BAN_TOTAL" },
361   { SNMP_DB_BAN_BANS_F_CLASS_BAN_COUNT, SNMP_DB_ID_BAN, 40,
362     sizeof(uint32_t), "BAN_BANS_F_CLASS_BAN_COUNT" },
363   { SNMP_DB_BAN_BANS_F_CLASS_BAN_TOTAL, SNMP_DB_ID_BAN, 44,
364     sizeof(uint32_t), "BAN_BANS_F_CLASS_BAN_TOTAL" },
365 
366   { 0, -1, 0, 0 }
367 };
368 
369 struct snmp_db_info {
370   int db_id;
371   int db_fd;
372   const char *db_name;
373   char *db_path;
374   void *db_data;
375   size_t db_datasz;
376 };
377 
378 static struct snmp_db_info snmp_dbs[] = {
379   { SNMP_DB_ID_UNKNOWN, -1, NULL, NULL, 0 },
380 
381   /* This "table" is synthetic; nothing to be persisted to disk. */
382   { SNMP_DB_ID_NOTIFY, -1, "notify.dat", NULL, NULL, 0 },
383 
384   /* This "table" is comprised purely of values in memory; nothing to be
385    * persisted to disk.
386    */
387   { SNMP_DB_ID_CONN, -1, "conn.dat", NULL, NULL, 0 },
388 
389   /* Eight numeric fields only in this table: 8 x 4 bytes = 32 bytes */
390   { SNMP_DB_ID_DAEMON, -1, "daemon.dat", NULL, NULL, 32 },
391 
392   /* The size of the timeouts table is calculated as:
393    *
394    *  4 timeout fields        x 4 bytes = 16 bytes
395    *
396    * for a total of 16 bytes.
397    */
398   { SNMP_DB_ID_TIMEOUTS, -1, "timeouts.dat", NULL, NULL, 16 },
399 
400   /* The size of the ftp table is calculated as:
401    *
402    *  3 session fields        x 4 bytes = 12 bytes
403    *  7 login fields          x 4 bytes = 28 bytes
404    *  11 data transfer fields x 4 bytes = 44 bytes
405    *
406    * for a total of 84 bytes.
407    */
408   { SNMP_DB_ID_FTP, -1, "ftp.dat", NULL, NULL, 84 },
409 
410   /* The size of the snmp table is calculated as:
411    *
412    *  4 fields                x 4 bytes = 20 bytes
413    */
414   { SNMP_DB_ID_SNMP, -1, "snmp.dat", NULL, NULL, 20 },
415 
416   /* The size of the ftps table is calculated as:
417    *
418    *  8 session fields        x 4 bytes = 32 bytes
419    *  6 login fields          x 4 bytes = 24 bytes
420    *  11 data transfer fields x 4 bytes = 44 bytes
421    *
422    * for a total of 100 bytes.
423    */
424   { SNMP_DB_ID_TLS, -1, "tls.dat", NULL, NULL, 100 },
425 
426   /* The size of the ssh table is calculated as:
427    *
428    *  3 session fields        x 4 bytes = 12 bytes
429    *  8 auth fields           x 4 bytes = 32 bytes
430    *
431    * for a total of 40 bytes.
432    */
433   { SNMP_DB_ID_SSH, -1, "ssh.dat", NULL, NULL, 44 },
434 
435   /* The size of the sftp table is calculated as:
436    *
437    *  6 session fields        x 4 bytes = 24 bytes
438    *  11 data transfer fields x 4 bytes = 44 bytes
439    *
440    * for a total of 68 bytes.
441    */
442   { SNMP_DB_ID_SFTP, -1, "sftp.dat", NULL, NULL, 68 },
443 
444   /* The size of the scp table is calculated as:
445    *
446    *  2 session fields        x 4 bytes =  8 bytes
447    *  8 data transfer fields  x 4 bytes = 32 bytes
448    *
449    * for a total of 40 bytes.
450    */
451   { SNMP_DB_ID_SCP, -1, "scp.dat", NULL, NULL, 40 },
452 
453   /* The size of the ban table is calculated as:
454    *
455    *  12 ban fields            x 4 bytes = 48 bytes
456    *
457    * for a total of 48 bytes.
458    */
459   { SNMP_DB_ID_BAN, -1, "ban.dat", NULL, NULL, 48 },
460 
461 #if 0
462   { SNMP_DB_ID_SQL, -1, "sql.dat", NULL, NULL, 0 },
463 
464   { SNMP_DB_ID_QUOTA, -1, "quota.dat", NULL, NULL, 0 },
465 
466   { SNMP_DB_ID_GEOIP, -1, "geoip.dat", NULL, NULL, 0 }
467 #endif
468 
469   { -1, -1, NULL, NULL, 0 },
470 };
471 
472 /* For the given field, provision the corresponding lock start and len
473  * values, for the byte-range locking.
474  */
get_field_range(unsigned int field,off_t * field_start,size_t * field_len)475 static int get_field_range(unsigned int field, off_t *field_start,
476     size_t *field_len) {
477   register unsigned int i;
478   int field_idx = -1;
479 
480   if (field_start == NULL &&
481       field_len == NULL) {
482     /* Nothing to do here. */
483     return 0;
484   }
485 
486   for (i = 0; snmp_fields[i].db_id > 0; i++) {
487     if (snmp_fields[i].field == field) {
488       field_idx = i;
489       break;
490     }
491   }
492 
493   if (field_idx < 0) {
494     errno = ENOENT;
495     return -1;
496   }
497 
498   if (field_start != NULL) {
499     *field_start = snmp_fields[field_idx].field_start;
500   }
501 
502   if (field_len != NULL) {
503     *field_len = snmp_fields[field_idx].field_len;
504   }
505 
506   return 0;
507 }
508 
get_lock_type(struct flock * lock)509 static const char *get_lock_type(struct flock *lock) {
510   const char *lock_type;
511 
512   switch (lock->l_type) {
513     case F_RDLCK:
514       lock_type = "read";
515       break;
516 
517     case F_WRLCK:
518       lock_type = "write";
519       break;
520 
521     case F_UNLCK:
522       lock_type = "unlock";
523       break;
524 
525     default:
526       lock_type = "[unknown]";
527   }
528 
529   return lock_type;
530 }
531 
snmp_db_get_field_db_id(unsigned int field)532 int snmp_db_get_field_db_id(unsigned int field) {
533   register unsigned int i;
534   int db_id = -1;
535 
536   for (i = 0; snmp_fields[i].db_id > 0; i++) {
537     if (snmp_fields[i].field == field) {
538       db_id = snmp_fields[i].db_id;
539       break;
540     }
541   }
542 
543   if (db_id < 0) {
544     errno = ENOENT;
545   }
546 
547   return db_id;
548 }
549 
snmp_db_get_fieldstr(pool * p,unsigned int field)550 const char *snmp_db_get_fieldstr(pool *p, unsigned int field) {
551   register unsigned int i;
552   char fieldstr[256];
553   int db_id = -1;
554   const char *db_name = NULL, *field_name = NULL;
555 
556   for (i = 0; snmp_fields[i].db_id > 0; i++) {
557     if (snmp_fields[i].field == field) {
558       db_id = snmp_fields[i].db_id;
559       field_name = snmp_fields[i].field_name;
560       break;
561     }
562   }
563 
564   if (db_id < 0) {
565     return NULL;
566   }
567 
568   db_name = snmp_dbs[db_id].db_name;
569 
570   memset(fieldstr, '\0', sizeof(fieldstr));
571   pr_snprintf(fieldstr, sizeof(fieldstr)-1, "%s (%d) [%s (%d)]",
572     field_name, field, db_name, db_id);
573   return pstrdup(p, fieldstr);
574 }
575 
snmp_db_rlock(unsigned int field)576 int snmp_db_rlock(unsigned int field) {
577   struct flock lock;
578   unsigned int nattempts = 1;
579   int db_id, db_fd;
580   size_t field_len;
581 
582   lock.l_type = F_RDLCK;
583   lock.l_whence = SEEK_SET;
584 
585   db_id = snmp_db_get_field_db_id(field);
586   if (db_id < 0) {
587     return -1;
588   }
589 
590   db_fd = snmp_dbs[db_id].db_fd;
591   if (get_field_range(field, &(lock.l_start), &field_len) < 0) {
592     return -1;
593   }
594   lock.l_len = (off_t) field_len;
595 
596   pr_trace_msg(trace_channel, 9,
597     "attempt #%u to read-lock field %u db ID %d table '%s' "
598     "(fd %d start %lu len %lu)", nattempts, field, db_id,
599     snmp_dbs[db_id].db_path, db_fd, (unsigned long) lock.l_start,
600     (unsigned long) lock.l_len);
601 
602   while (fcntl(db_fd, F_SETLK, &lock) < 0) {
603     int xerrno = errno;
604 
605     if (xerrno == EINTR) {
606       pr_signals_handle();
607       continue;
608     }
609 
610     pr_trace_msg(trace_channel, 3, "read-lock of table fd %d failed: %s",
611       db_fd, strerror(xerrno));
612     if (xerrno == EACCES) {
613       struct flock locker;
614 
615       /* Get the PID of the process blocking this lock. */
616       if (fcntl(db_fd, F_GETLK, &locker) == 0) {
617         pr_trace_msg(trace_channel, 3, "process ID %lu has blocking %s lock on "
618           "table fd %d, start %lu len %lu", (unsigned long) locker.l_pid,
619           get_lock_type(&locker), db_fd, (unsigned long) lock.l_start,
620           (unsigned long) lock.l_len);
621       }
622     }
623 
624     if (xerrno == EAGAIN ||
625         xerrno == EACCES) {
626       /* Treat this as an interrupted call, call pr_signals_handle() (which
627        * will delay for a few msecs because of EINTR), and try again.
628        * After SNMP_MAX_LOCK_ATTEMPTS attempts, give up altogether.
629        */
630 
631       nattempts++;
632       if (nattempts <= SNMP_MAX_LOCK_ATTEMPTS) {
633         errno = EINTR;
634 
635         pr_signals_handle();
636 
637         errno = 0;
638         pr_trace_msg(trace_channel, 9,
639           "attempt #%u to read-lock table fd %d", nattempts, db_fd);
640         continue;
641       }
642 
643       pr_trace_msg(trace_channel, 3,
644         "unable to acquire read-lock on table fd %d: %s", db_fd,
645         strerror(xerrno));
646     }
647 
648     errno = xerrno;
649     return -1;
650   }
651 
652   pr_trace_msg(trace_channel, 9,
653     "read-lock of field %u table fd %d (start %lu len %lu) successful",
654     field, db_fd, (unsigned long) lock.l_start, (unsigned long) lock.l_len);
655   return 0;
656 }
657 
snmp_db_wlock(unsigned int field)658 int snmp_db_wlock(unsigned int field) {
659   struct flock lock;
660   unsigned int nattempts = 1;
661   int db_id, db_fd;
662   size_t field_len;
663 
664   lock.l_type = F_WRLCK;
665   lock.l_whence = SEEK_SET;
666 
667   db_id = snmp_db_get_field_db_id(field);
668   if (db_id < 0) {
669     return -1;
670   }
671 
672   db_fd = snmp_dbs[db_id].db_fd;
673   if (get_field_range(field, &(lock.l_start), &field_len) < 0) {
674     return -1;
675   }
676   lock.l_len = (off_t) field_len;
677 
678   pr_trace_msg(trace_channel, 9,
679     "attempt #%u to write-lock field %u db ID %d table '%s' "
680     "(fd %d start %lu len %lu)", nattempts, field, db_id,
681     snmp_dbs[db_id].db_path, db_fd, (unsigned long) lock.l_start,
682     (unsigned long) lock.l_len);
683 
684   while (fcntl(db_fd, F_SETLK, &lock) < 0) {
685     int xerrno = errno;
686 
687     if (xerrno == EINTR) {
688       pr_signals_handle();
689       continue;
690     }
691 
692     pr_trace_msg(trace_channel, 3, "write-lock of table fd %d failed: %s",
693       db_fd, strerror(xerrno));
694     if (xerrno == EACCES) {
695       struct flock locker;
696 
697       /* Get the PID of the process blocking this lock. */
698       if (fcntl(db_fd, F_GETLK, &locker) == 0) {
699         pr_trace_msg(trace_channel, 3, "process ID %lu has blocking %s lock on "
700           "table fd %d, start %lu len %lu", (unsigned long) locker.l_pid,
701           get_lock_type(&locker), db_fd, (unsigned long) lock.l_start,
702           (unsigned long) lock.l_len);
703       }
704     }
705 
706     if (xerrno == EAGAIN ||
707         xerrno == EACCES) {
708       /* Treat this as an interrupted call, call pr_signals_handle() (which
709        * will delay for a few msecs because of EINTR), and try again.
710        * After SNMP_MAX_LOCK_ATTEMPTS attempts, give up altogether.
711        */
712 
713       nattempts++;
714       if (nattempts <= SNMP_MAX_LOCK_ATTEMPTS) {
715         errno = EINTR;
716 
717         pr_signals_handle();
718 
719         errno = 0;
720         pr_trace_msg(trace_channel, 9,
721           "attempt #%u to write-lock table fd %d", nattempts, db_fd);
722         continue;
723       }
724 
725       pr_trace_msg(trace_channel, 3,
726         "unable to acquire write-lock on table fd %d: %s", db_fd,
727         strerror(xerrno));
728     }
729 
730     errno = xerrno;
731     return -1;
732   }
733 
734   pr_trace_msg(trace_channel, 9,
735     "write-lock of field %u table fd %d (start %lu len %lu) successful",
736     field, db_fd, (unsigned long) lock.l_start, (unsigned long) lock.l_len);
737   return 0;
738 }
739 
snmp_db_unlock(unsigned int field)740 int snmp_db_unlock(unsigned int field) {
741   struct flock lock;
742   unsigned int nattempts = 1;
743   int db_id, db_fd;
744   size_t field_len;
745 
746   lock.l_type = F_UNLCK;
747   lock.l_whence = SEEK_SET;
748 
749   db_id = snmp_db_get_field_db_id(field);
750   if (db_id < 0) {
751     return -1;
752   }
753 
754   db_fd = snmp_dbs[db_id].db_fd;
755   if (get_field_range(field, &(lock.l_start), &field_len) < 0) {
756     return -1;
757   }
758   lock.l_len = (off_t) field_len;
759 
760   pr_trace_msg(trace_channel, 9,
761     "attempt #%u to unlock field %u table '%s' (fd %d start %lu len %lu)",
762     nattempts, field, snmp_dbs[db_id].db_path, db_fd,
763     (unsigned long) lock.l_start, (unsigned long) lock.l_len);
764 
765   while (fcntl(db_fd, F_SETLK, &lock) < 0) {
766     int xerrno = errno;
767 
768     if (xerrno == EINTR) {
769       pr_signals_handle();
770       continue;
771     }
772 
773     pr_trace_msg(trace_channel, 3, "unlock of table fd %d failed: %s",
774       db_fd, strerror(xerrno));
775     if (xerrno == EACCES) {
776       struct flock locker;
777 
778       /* Get the PID of the process blocking this lock. */
779       if (fcntl(db_fd, F_GETLK, &locker) == 0) {
780         pr_trace_msg(trace_channel, 3, "process ID %lu has blocking %s lock on "
781           "table fd %d, start %lu len %lu", (unsigned long) locker.l_pid,
782           get_lock_type(&locker), db_fd, (unsigned long) lock.l_start,
783           (unsigned long) lock.l_len);
784       }
785     }
786 
787     if (xerrno == EAGAIN ||
788         xerrno == EACCES) {
789       /* Treat this as an interrupted call, call pr_signals_handle() (which
790        * will delay for a few msecs because of EINTR), and try again.
791        * After SNMP_MAX_LOCK_ATTEMPTS attempts, give up altogether.
792        */
793 
794       nattempts++;
795       if (nattempts <= SNMP_MAX_LOCK_ATTEMPTS) {
796         errno = EINTR;
797 
798         pr_signals_handle();
799 
800         errno = 0;
801         pr_trace_msg(trace_channel, 9,
802           "attempt #%u to unlock table fd %d", nattempts, db_fd);
803         continue;
804       }
805 
806       pr_trace_msg(trace_channel, 3,
807         "unable to acquire unlock on table fd %d: %s", db_fd,
808         strerror(xerrno));
809     }
810 
811     errno = xerrno;
812     return -1;
813   }
814 
815   pr_trace_msg(trace_channel, 9,
816     "unlock of field %u table fd %d (start %lu len %lu) successful",
817     field, db_fd, (unsigned long) lock.l_start, (unsigned long) lock.l_len);
818   return 0;
819 }
820 
snmp_db_open(pool * p,int db_id)821 int snmp_db_open(pool *p, int db_id) {
822   int db_fd, mmap_flags, xerrno;
823   char *db_path;
824   size_t db_datasz;
825   void *db_data;
826 
827   if (db_id < 0) {
828     errno = EINVAL;
829     return -1;
830   }
831 
832   /* First, see if the database is already opened. */
833   if (snmp_dbs[db_id].db_path != NULL) {
834     return 0;
835   }
836 
837   pr_trace_msg(trace_channel, 19,
838     "opening db ID %d (db root = %s, db name = %s)", db_id, snmp_db_root,
839     snmp_dbs[db_id].db_name);
840 
841   db_path = pdircat(p, snmp_db_root, snmp_dbs[db_id].db_name, NULL);
842 
843   PRIVS_ROOT
844   db_fd = open(db_path, O_RDWR|O_CREAT, 0600);
845   xerrno = errno;
846   PRIVS_RELINQUISH
847 
848   if (db_fd < 0) {
849     (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
850       "error opening SNMPTable '%s': %s", db_path, strerror(xerrno));
851     errno = xerrno;
852     return -1;
853   }
854 
855   /* Make sure the fd isn't one of the big three. */
856   (void) pr_fs_get_usable_fd2(&db_fd);
857 
858   pr_trace_msg(trace_channel, 19, "opened fd %d for SNMPTable '%s'", db_fd,
859     db_path);
860 
861   snmp_dbs[db_id].db_fd = db_fd;
862   snmp_dbs[db_id].db_path = db_path;
863 
864   db_datasz = snmp_dbs[db_id].db_datasz;
865 
866   /* Truncate the table first; any existing data should be deleted. */
867   if (ftruncate(db_fd, 0) < 0) {
868     xerrno = errno;
869 
870     pr_trace_msg(trace_channel, 1,
871       "error truncating SNMPTable '%s' to size 0: %s", db_path,
872       strerror(xerrno));
873 
874     (void) snmp_db_close(p, db_id);
875     errno = xerrno;
876     return -1;
877   }
878 
879   /* Seek to the desired table size (actually, one byte less than the desired
880    * size) and write a single byte, so that there's enough allocated backing
881    * store on the filesystem to support the ensuing mmap() call.
882    */
883   if (lseek(db_fd, db_datasz, SEEK_SET) < 0) {
884     xerrno = errno;
885 
886     pr_trace_msg(trace_channel, 1,
887       "error seeking to %lu in table '%s': %s",
888       (unsigned long) db_datasz-1, db_path, strerror(xerrno));
889 
890     (void) snmp_db_close(p, db_id);
891     errno = xerrno;
892     return -1;
893   }
894 
895   if (write(db_fd, "", 1) != 1) {
896     xerrno = errno;
897 
898     pr_trace_msg(trace_channel, 1,
899       "error writing single byte to table '%s': %s", db_path, strerror(xerrno));
900 
901     (void) snmp_db_close(p, db_id);
902     errno = xerrno;
903     return -1;
904   }
905 
906   mmap_flags = MAP_SHARED;
907 
908   /* Make sure to set the fd to -1 if MAP_ANON(YMOUS) is used.  By definition,
909    * anonymous mapped memory does not need (or want) a valid file backing
910    * store; some implementations will not do what is expected when anonymous
911    * memory is requested AND a valid fd is passed in.
912    *
913    * However, we want to keep a valid fd open anyway, for later use by
914    * fcntl(2) for byte range locking; we simply don't use the valid fd for
915    * the mmap(2) call.
916    */
917 
918 #if defined(MAP_ANONYMOUS)
919   /* Linux */
920   mmap_flags |= MAP_ANONYMOUS;
921   db_fd = -1;
922 
923 #elif defined(MAP_ANON)
924   /* FreeBSD, MacOSX, Solaris, others? */
925   mmap_flags |= MAP_ANON;
926   db_fd = -1;
927 
928 #else
929   (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
930     "mmap(2) MAP_ANONYMOUS and MAP_ANON flags not defined");
931 #endif
932 
933   db_data = mmap(NULL, db_datasz, PROT_READ|PROT_WRITE, mmap_flags, db_fd, 0);
934   if (db_data == MAP_FAILED) {
935     xerrno = errno;
936 
937     pr_trace_msg(trace_channel, 1,
938       "error mapping table '%s' fd %d size %lu into memory: %s", db_path,
939       db_fd, (unsigned long) db_datasz, strerror(xerrno));
940 
941     (void) snmp_db_close(p, db_id);
942     errno = xerrno;
943     return -1;
944   }
945 
946   snmp_dbs[db_id].db_data = db_data;
947 
948   /* Make sure the data are zeroed. */
949   memset(db_data, 0, db_datasz);
950 
951   return 0;
952 }
953 
snmp_db_close(pool * p,int db_id)954 int snmp_db_close(pool *p, int db_id) {
955   int db_fd, res;
956   void *db_data;
957 
958   if (db_id < 0) {
959     errno = EINVAL;
960     return -1;
961   }
962 
963   db_data = snmp_dbs[db_id].db_data;
964 
965   if (db_data != NULL) {
966     size_t db_datasz;
967 
968     db_datasz = snmp_dbs[db_id].db_datasz;
969 
970     if (munmap(db_data, db_datasz) < 0) {
971       int xerrno = errno;
972 
973       pr_trace_msg(trace_channel, 1,
974         "error unmapping SNMPTable '%s' from memory: %s",
975         pdircat(p, snmp_db_root, snmp_dbs[db_id].db_path, NULL),
976         strerror(xerrno));
977 
978       errno = xerrno;
979       return -1;
980     }
981   }
982 
983   snmp_dbs[db_id].db_data = NULL;
984 
985   db_fd = snmp_dbs[db_id].db_fd;
986   res = close(db_fd);
987   if (res < 0) {
988     return -1;
989   }
990 
991   snmp_dbs[db_id].db_fd = -1;
992   return 0;
993 }
994 
snmp_db_get_value(pool * p,unsigned int field,int32_t * int_value,char ** str_value,size_t * str_valuelen)995 int snmp_db_get_value(pool *p, unsigned int field, int32_t *int_value,
996     char **str_value, size_t *str_valuelen) {
997   void *db_data, *field_data;
998   int db_id, res;
999   off_t field_start;
1000   size_t field_len;
1001 
1002   switch (field) {
1003     case SNMP_DB_NOTIFY_F_SYS_UPTIME: {
1004       struct timeval start_tv, now_tv;
1005 
1006       /* TimeTicks are in hundredths of seconds since start time. */
1007       res = snmp_uptime_get(p, &start_tv);
1008       if (res < 0)
1009         return -1;
1010 
1011       gettimeofday(&now_tv, NULL);
1012 
1013       *int_value = (int32_t) (((now_tv.tv_sec - start_tv.tv_sec) * 100) +
1014         ((now_tv.tv_usec - start_tv.tv_usec) / 10000));
1015 
1016       pr_trace_msg(trace_channel, 19,
1017         "read value %lu for field %s", (unsigned long) *int_value,
1018         snmp_db_get_fieldstr(p, field));
1019       return 0;
1020     }
1021 
1022     case SNMP_DB_CONN_F_SERVER_NAME:
1023       if (main_server->ServerName == NULL) {
1024         errno = ENOENT;
1025         return -1;
1026       }
1027 
1028       *str_value = (char *) main_server->ServerName;
1029       *str_valuelen = strlen(*str_value);
1030 
1031       pr_trace_msg(trace_channel, 19,
1032         "read value '%s' for field %s", *str_value,
1033         snmp_db_get_fieldstr(p, field));
1034       return 0;
1035 
1036     case SNMP_DB_CONN_F_SERVER_ADDR:
1037       if (session.c == NULL) {
1038         errno = ENOENT;
1039         return -1;
1040       }
1041 
1042       *str_value = (char *) pr_netaddr_get_ipstr(session.c->local_addr);
1043       *str_valuelen = strlen(*str_value);
1044 
1045       pr_trace_msg(trace_channel, 19,
1046         "read value '%s' for field %s", *str_value,
1047         snmp_db_get_fieldstr(p, field));
1048       return 0;
1049 
1050     case SNMP_DB_CONN_F_SERVER_PORT:
1051       if (session.c == NULL) {
1052         errno = ENOENT;
1053         return -1;
1054       }
1055 
1056       *int_value = ntohs(pr_netaddr_get_port(session.c->remote_addr));
1057       pr_trace_msg(trace_channel, 19,
1058         "read value %lu for field %s", (unsigned long) *int_value,
1059         snmp_db_get_fieldstr(p, field));
1060       return 0;
1061 
1062     case SNMP_DB_CONN_F_CLIENT_ADDR:
1063       if (session.c == NULL) {
1064         errno = ENOENT;
1065         return -1;
1066       }
1067 
1068       *str_value = (char *) pr_netaddr_get_ipstr(session.c->remote_addr);
1069       *str_valuelen = strlen(*str_value);
1070 
1071       pr_trace_msg(trace_channel, 19,
1072         "read value '%s' for field %s", *str_value,
1073         snmp_db_get_fieldstr(p, field));
1074       return 0;
1075 
1076     case SNMP_DB_CONN_F_PID:
1077       *int_value = session.pid;
1078       pr_trace_msg(trace_channel, 19,
1079         "read value %lu for field %s", (unsigned long) *int_value,
1080         snmp_db_get_fieldstr(p, field));
1081       return 0;
1082 
1083     case SNMP_DB_CONN_F_USER_NAME: {
1084       const char *orig_user;
1085 
1086       orig_user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
1087       if (orig_user == NULL) {
1088         errno = ENOENT;
1089         return -1;
1090       }
1091 
1092       *str_value = (char *) orig_user;
1093       *str_valuelen = strlen(*str_value);
1094 
1095       pr_trace_msg(trace_channel, 19,
1096         "read value '%s' for field %s", *str_value,
1097         snmp_db_get_fieldstr(p, field));
1098       return 0;
1099     }
1100 
1101     case SNMP_DB_CONN_F_PROTOCOL: {
1102       const char *proto;
1103 
1104       proto = pr_session_get_protocol(0);
1105 
1106       *str_value = (char *) proto;
1107       *str_valuelen = strlen(*str_value);
1108 
1109       pr_trace_msg(trace_channel, 19,
1110         "read value '%s' for field %s", *str_value,
1111         snmp_db_get_fieldstr(p, field));
1112       return 0;
1113     }
1114 
1115     case SNMP_DB_DAEMON_F_SOFTWARE:
1116       *str_value = "proftpd";
1117       *str_valuelen = strlen(*str_value);
1118 
1119       pr_trace_msg(trace_channel, 19,
1120         "read value '%s' for field %s", *str_value,
1121         snmp_db_get_fieldstr(p, field));
1122       return 0;
1123 
1124     case SNMP_DB_DAEMON_F_VERSION:
1125       *str_value = "ProFTPD Version " PROFTPD_VERSION_TEXT " (built at " BUILD_STAMP ")";
1126       *str_valuelen = strlen(*str_value);
1127 
1128       pr_trace_msg(trace_channel, 19,
1129         "read value '%s' for field %s", *str_value,
1130         snmp_db_get_fieldstr(p, field));
1131       return 0;
1132 
1133     case SNMP_DB_DAEMON_F_ADMIN:
1134       *str_value = (char *) main_server->ServerAdmin;
1135       *str_valuelen = strlen(*str_value);
1136 
1137       pr_trace_msg(trace_channel, 19,
1138         "read value '%s' for field %s", *str_value,
1139         snmp_db_get_fieldstr(p, field));
1140       return 0;
1141 
1142     case SNMP_DB_DAEMON_F_UPTIME: {
1143       struct timeval now_tv;
1144 
1145       /* TimeTicks are in hundredths of seconds since start time. */
1146       gettimeofday(&now_tv, NULL);
1147 
1148       *int_value = (int32_t) (((now_tv.tv_sec - snmp_start_tv.tv_sec) * 100) +
1149         ((now_tv.tv_usec - snmp_start_tv.tv_usec) / 10000));
1150 
1151       pr_trace_msg(trace_channel, 19,
1152         "read value %lu for field %s", (unsigned long) *int_value,
1153         snmp_db_get_fieldstr(p, field));
1154       return 0;
1155     }
1156 
1157     case SNMP_DB_DAEMON_F_MAXINST_CONF:
1158       *int_value = ServerMaxInstances;
1159 
1160       pr_trace_msg(trace_channel, 19,
1161         "read value %lu for field %s", (unsigned long) *int_value,
1162         snmp_db_get_fieldstr(p, field));
1163       return 0;
1164 
1165     default:
1166       break;
1167   }
1168 
1169   db_id = snmp_db_get_field_db_id(field);
1170   if (db_id < 0) {
1171     return -1;
1172   }
1173 
1174   if (get_field_range(field, &field_start, &field_len) < 0) {
1175     return -1;
1176   }
1177 
1178   res = snmp_db_rlock(field);
1179   if (res < 0) {
1180     return -1;
1181   }
1182 
1183   db_data = snmp_dbs[db_id].db_data;
1184   field_data = &(((uint32_t *) db_data)[field_start]);
1185   memmove(int_value, field_data, field_len);
1186 
1187   res = snmp_db_unlock(field);
1188   if (res < 0) {
1189     return -1;
1190   }
1191 
1192   pr_trace_msg(trace_channel, 19,
1193     "read value %lu for field %s", (unsigned long) *int_value,
1194      snmp_db_get_fieldstr(p, field));
1195   return 0;
1196 }
1197 
snmp_db_incr_value(pool * p,unsigned int field,int32_t incr)1198 int snmp_db_incr_value(pool *p, unsigned int field, int32_t incr) {
1199   uint32_t orig_val, new_val;
1200   int db_id, res;
1201   void *db_data, *field_data;
1202   off_t field_start;
1203   size_t field_len;
1204 
1205   db_id = snmp_db_get_field_db_id(field);
1206   if (db_id < 0) {
1207     return -1;
1208   }
1209 
1210   if (get_field_range(field, &field_start, &field_len) < 0) {
1211     return -1;
1212   }
1213 
1214   res = snmp_db_wlock(field);
1215   if (res < 0) {
1216     return -1;
1217   }
1218 
1219   db_data = snmp_dbs[db_id].db_data;
1220   field_data = &(((uint32_t *) db_data)[field_start]);
1221   memmove(&new_val, field_data, field_len);
1222   orig_val = new_val;
1223 
1224   if (orig_val == 0 &&
1225       incr < 0) {
1226     /* If we are in fact decrementing a value, and that value is
1227      * already zero, then do nothing.
1228      */
1229 
1230     res = snmp_db_unlock(field);
1231     if (res < 0) {
1232       return -1;
1233     }
1234 
1235     pr_trace_msg(trace_channel, 19,
1236       "value already zero for field %s (%d), not decrementing by %ld",
1237       snmp_db_get_fieldstr(p, field), field, (long) incr);
1238     return 0;
1239   }
1240 
1241   new_val += incr;
1242   memmove(field_data, &new_val, field_len);
1243 
1244 #if 0
1245   res = msync(field_data, field_len, MS_SYNC);
1246   if (res < 0) {
1247     pr_trace_msg(trace_channel, 1, "msync(2) error for field %s (%d): %s",
1248       snmp_db_get_fieldstr(p, field), field, strerror(errno));
1249   }
1250 #endif
1251 
1252   res = snmp_db_unlock(field);
1253   if (res < 0) {
1254     return -1;
1255   }
1256 
1257   pr_trace_msg(trace_channel, 19,
1258     "wrote value %lu (was %lu) for field %s (%d)", (unsigned long) new_val,
1259     (unsigned long) orig_val, snmp_db_get_fieldstr(p, field), field);
1260   return 0;
1261 }
1262 
snmp_db_reset_value(pool * p,unsigned int field)1263 int snmp_db_reset_value(pool *p, unsigned int field) {
1264   uint32_t val;
1265   int db_id, res;
1266   void *db_data, *field_data;
1267   off_t field_start;
1268   size_t field_len;
1269 
1270   db_id = snmp_db_get_field_db_id(field);
1271   if (db_id < 0) {
1272     return -1;
1273   }
1274 
1275   if (get_field_range(field, &field_start, &field_len) < 0) {
1276     return -1;
1277   }
1278 
1279   res = snmp_db_wlock(field);
1280   if (res < 0) {
1281     return -1;
1282   }
1283 
1284   db_data = snmp_dbs[db_id].db_data;
1285   field_data = &(((uint32_t *) db_data)[field_start]);
1286 
1287   val = 0;
1288   memmove(field_data, &val, field_len);
1289 
1290   res = snmp_db_unlock(field);
1291   if (res < 0) {
1292     return -1;
1293   }
1294 
1295   pr_trace_msg(trace_channel, 19,
1296     "reset value to 0 for field %s", snmp_db_get_fieldstr(p, field));
1297   return 0;
1298 }
1299 
snmp_db_set_root(const char * db_root)1300 int snmp_db_set_root(const char *db_root) {
1301   if (db_root == NULL) {
1302     errno = EINVAL;
1303     return -1;
1304   }
1305 
1306   snmp_db_root = db_root;
1307   return 0;
1308 }
1309