1 /*
2  * ProFTPD - FTP server daemon
3  * Copyright (c) 2008-2017 The ProFTPD Project team
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, The ProFTPD Project and other respective copyright
20  * holders give permission to link this program with OpenSSL, and distribute
21  * the resulting executable, without including the source code for OpenSSL in
22  * the source distribution.
23  */
24 
25 /* TransferRate throttling */
26 
27 #include "conf.h"
28 
29 /* Transfer rate variables */
30 static long double xfer_rate_kbps = 0.0, xfer_rate_bps = 0.0;
31 static off_t xfer_rate_freebytes = 0.0;
32 static int have_xfer_rate = FALSE;
33 static unsigned int xfer_rate_scoreboard_updates = 0;
34 
35 /* Very similar to the {block,unblock}_signals() function, this masks most
36  * of the same signals -- except for TERM.  This allows a throttling process
37  * to be killed by the admin.
38  */
xfer_rate_sigmask(int block)39 static void xfer_rate_sigmask(int block) {
40   static sigset_t sig_set;
41 
42   if (block) {
43     sigemptyset(&sig_set);
44 
45     sigaddset(&sig_set, SIGCHLD);
46     sigaddset(&sig_set, SIGUSR1);
47     sigaddset(&sig_set, SIGINT);
48     sigaddset(&sig_set, SIGQUIT);
49 #ifdef SIGIO
50     sigaddset(&sig_set, SIGIO);
51 #endif /* SIGIO */
52 #ifdef SIGBUS
53     sigaddset(&sig_set, SIGBUS);
54 #endif /* SIGBUS */
55     sigaddset(&sig_set, SIGHUP);
56 
57     while (sigprocmask(SIG_BLOCK, &sig_set, NULL) < 0) {
58       if (errno == EINTR) {
59         pr_signals_handle();
60         continue;
61       }
62 
63       break;
64     }
65 
66   } else {
67     while (sigprocmask(SIG_UNBLOCK, &sig_set, NULL) < 0) {
68       if (errno == EINTR) {
69         pr_signals_handle();
70         continue;
71       }
72 
73       break;
74     }
75   }
76 }
77 
78 /* Returns the difference, in milliseconds, between the given timeval and
79  * now.
80  */
xfer_rate_since(struct timeval * then)81 static long xfer_rate_since(struct timeval *then) {
82   struct timeval now;
83   gettimeofday(&now, NULL);
84 
85   return (((now.tv_sec - then->tv_sec) * 1000L) +
86     ((now.tv_usec - then->tv_usec) / 1000L));
87 }
88 
pr_throttle_have_rate(void)89 int pr_throttle_have_rate(void) {
90   return have_xfer_rate;
91 }
92 
pr_throttle_init(cmd_rec * cmd)93 void pr_throttle_init(cmd_rec *cmd) {
94   config_rec *c = NULL;
95   char *xfer_cmd = NULL;
96   unsigned char have_user_rate = FALSE, have_group_rate = FALSE,
97     have_class_rate = FALSE;
98   unsigned int precedence = 0;
99 
100   /* Make sure the variables are (re)initialized */
101   xfer_rate_kbps = xfer_rate_bps = 0.0;
102   xfer_rate_freebytes = 0;
103   xfer_rate_scoreboard_updates = 0;
104   have_xfer_rate = FALSE;
105 
106   c = find_config(CURRENT_CONF, CONF_PARAM, "TransferRate", FALSE);
107 
108   /* Note: need to cycle through all the matching config_recs, and using
109    * the information from the current config_rec only if it matches
110    * the target *and* has a higher precedence than any of the previously
111    * found config_recs.
112    */
113   while (c) {
114     char **cmdlist = (char **) c->argv[0];
115     int matched_cmd = FALSE;
116 
117     pr_signals_handle();
118 
119     /* Does this TransferRate apply to the current command?  Note: this
120      * could be made more efficient by using bitmasks rather than string
121      * comparisons.
122      */
123     for (xfer_cmd = *cmdlist; xfer_cmd; xfer_cmd = *(cmdlist++)) {
124       if (strcasecmp(xfer_cmd, cmd->argv[0]) == 0) {
125         matched_cmd = TRUE;
126         break;
127       }
128     }
129 
130     /* No -- continue on to the next TransferRate. */
131     if (!matched_cmd) {
132       c = find_config_next(c, c->next, CONF_PARAM, "TransferRate", FALSE);
133       continue;
134     }
135 
136     if (c->argc > 4) {
137       if (strncmp(c->argv[4], "user", 5) == 0) {
138 
139         if (pr_expr_eval_user_or((char **) &c->argv[5]) == TRUE &&
140             *((unsigned int *) c->argv[3]) > precedence) {
141 
142           /* Set the precedence. */
143           precedence = *((unsigned int *) c->argv[3]);
144 
145           xfer_rate_kbps = *((long double *) c->argv[1]);
146           xfer_rate_freebytes = *((off_t *) c->argv[2]);
147           have_xfer_rate = TRUE;
148           have_user_rate = TRUE;
149           have_group_rate = have_class_rate = FALSE;
150         }
151 
152       } else if (strncmp(c->argv[4], "group", 6) == 0) {
153 
154         if (pr_expr_eval_group_and((char **) &c->argv[5]) == TRUE &&
155             *((unsigned int *) c->argv[3]) > precedence) {
156 
157           /* Set the precedence. */
158           precedence = *((unsigned int *) c->argv[3]);
159 
160           xfer_rate_kbps = *((long double *) c->argv[1]);
161           xfer_rate_freebytes = *((off_t *) c->argv[2]);
162           have_xfer_rate = TRUE;
163           have_group_rate = TRUE;
164           have_user_rate = have_class_rate = FALSE;
165         }
166 
167       } else if (strncmp(c->argv[4], "class", 6) == 0) {
168 
169         if (pr_expr_eval_class_or((char **) &c->argv[5]) == TRUE &&
170           *((unsigned int *) c->argv[3]) > precedence) {
171 
172           /* Set the precedence. */
173           precedence = *((unsigned int *) c->argv[3]);
174 
175           xfer_rate_kbps = *((long double *) c->argv[1]);
176           xfer_rate_freebytes = *((off_t *) c->argv[2]);
177           have_xfer_rate = TRUE;
178           have_class_rate = TRUE;
179           have_user_rate = have_group_rate = FALSE;
180         }
181       }
182 
183     } else {
184 
185       if (*((unsigned int *) c->argv[3]) > precedence) {
186 
187         /* Set the precedence. */
188         precedence = *((unsigned int *) c->argv[3]);
189 
190         xfer_rate_kbps = *((long double *) c->argv[1]);
191         xfer_rate_freebytes = *((off_t *) c->argv[2]);
192         have_xfer_rate = TRUE;
193         have_user_rate = have_group_rate = have_class_rate = FALSE;
194       }
195     }
196 
197     c = find_config_next(c, c->next, CONF_PARAM, "TransferRate", FALSE);
198   }
199 
200   /* Print out a helpful debugging message. */
201   if (have_xfer_rate) {
202     pr_log_debug(DEBUG3, "TransferRate (%.3Lf KB/s, %" PR_LU
203         " bytes free) in effect%s", xfer_rate_kbps,
204       (pr_off_t) xfer_rate_freebytes,
205       have_user_rate ? " for current user" :
206       have_group_rate ? " for current group" :
207       have_class_rate ? " for current class" : "");
208 
209     /* Convert the configured Kbps to bytes per usec, for use later.
210      * The 1024.0 factor converts for Kbytes to bytes, and the
211      * 1000000.0 factor converts from secs to usecs.
212      */
213     xfer_rate_bps = xfer_rate_kbps * 1024.0;
214   }
215 }
216 
pr_throttle_pause(off_t xferlen,int xfer_ending)217 void pr_throttle_pause(off_t xferlen, int xfer_ending) {
218   long ideal = 0, elapsed = 0;
219   off_t orig_xferlen = xferlen;
220 
221   if (XFER_ABORTED) {
222     return;
223   }
224 
225   /* Calculate the time interval since the transfer of data started. */
226   elapsed = xfer_rate_since(&session.xfer.start_time);
227 
228   /* Perform no throttling if no throttling has been configured. */
229   if (!have_xfer_rate) {
230     xfer_rate_scoreboard_updates++;
231 
232     if (xfer_ending ||
233         xfer_rate_scoreboard_updates % PR_TUNABLE_XFER_SCOREBOARD_UPDATES == 0) {
234       /* Update the scoreboard. */
235       pr_scoreboard_entry_update(session.pid,
236         PR_SCORE_XFER_LEN, orig_xferlen,
237         PR_SCORE_XFER_ELAPSED, (unsigned long) elapsed,
238         NULL);
239 
240       xfer_rate_scoreboard_updates = 0;
241     }
242 
243     return;
244   }
245 
246   /* Give credit for any configured freebytes. */
247   if (xferlen > 0 &&
248       xfer_rate_freebytes > 0) {
249 
250     if (xferlen > xfer_rate_freebytes) {
251       /* Decrement the number of bytes transferred by the freebytes, so that
252        * any throttling does not take into account the freebytes.
253        */
254       xferlen -= xfer_rate_freebytes;
255 
256     } else {
257       xfer_rate_scoreboard_updates++;
258 
259       /* The number of bytes transferred is less than the freebytes.  Just
260        * update the scoreboard -- no throttling needed.
261        */
262 
263       if (xfer_ending ||
264           xfer_rate_scoreboard_updates % PR_TUNABLE_XFER_SCOREBOARD_UPDATES == 0) {
265         pr_scoreboard_entry_update(session.pid,
266           PR_SCORE_XFER_LEN, orig_xferlen,
267           PR_SCORE_XFER_ELAPSED, (unsigned long) elapsed,
268           NULL);
269 
270         xfer_rate_scoreboard_updates = 0;
271       }
272 
273       return;
274     }
275   }
276 
277   ideal = xferlen * 1000L / xfer_rate_bps;
278 
279   if (ideal > elapsed) {
280     struct timeval tv;
281 
282     /* Setup for the select.  We use select() instead of usleep() because it
283      * seems to be far more portable across platforms.
284      *
285      * ideal and elapsed are in milleconds, but tv_usec will be microseconds,
286      * so be sure to convert properly.
287      */
288     tv.tv_usec = (ideal - elapsed) * 1000;
289     tv.tv_sec = tv.tv_usec / 1000000L;
290     tv.tv_usec = tv.tv_usec % 1000000L;
291 
292     pr_log_debug(DEBUG7, "transferring too fast, delaying %ld sec%s, %ld usecs",
293       (long int) tv.tv_sec, tv.tv_sec == 1 ? "" : "s", (long int) tv.tv_usec);
294 
295     /* No interruptions, please... */
296     xfer_rate_sigmask(TRUE);
297 
298     if (select(0, NULL, NULL, NULL, &tv) < 0) {
299       int xerrno = errno;
300 
301       if (XFER_ABORTED) {
302         pr_log_pri(PR_LOG_NOTICE, "throttling interrupted, transfer aborted");
303         xfer_rate_sigmask(FALSE);
304         return;
305       }
306 
307       /* At this point, we've probably been interrupted by one of the few
308        * signals not masked off, e.g. SIGTERM.
309        */
310       if (xerrno != EINTR) {
311         pr_log_debug(DEBUG0, "unable to throttle bandwidth: %s",
312           strerror(xerrno));
313       }
314     }
315 
316     xfer_rate_sigmask(FALSE);
317     pr_signals_handle();
318 
319     /* Update the scoreboard. */
320     pr_scoreboard_entry_update(session.pid,
321       PR_SCORE_XFER_LEN, orig_xferlen,
322       PR_SCORE_XFER_ELAPSED, (unsigned long) ideal,
323       NULL);
324 
325   } else {
326 
327     /* Update the scoreboard. */
328     pr_scoreboard_entry_update(session.pid,
329       PR_SCORE_XFER_LEN, orig_xferlen,
330       PR_SCORE_XFER_ELAPSED, (unsigned long) elapsed,
331       NULL);
332   }
333 
334   return;
335 }
336