001: /*
002: * This file is part of DrFTPD, Distributed FTP Daemon.
003: *
004: * DrFTPD is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * DrFTPD is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with DrFTPD; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018: package org.drftpd.master;
019:
020: import java.io.FileInputStream;
021: import java.io.IOException;
022: import java.io.PrintWriter;
023: import java.net.InetSocketAddress;
024: import java.net.ServerSocket;
025: import java.net.Socket;
026: import java.util.ArrayList;
027: import java.util.Collections;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.Properties;
031: import java.util.TimerTask;
032: import javax.net.ssl.*;
033: import org.drftpd.SSLGetContext;
034:
035: import net.sf.drftpd.FatalException;
036: import net.sf.drftpd.event.Event;
037: import net.sf.drftpd.master.BaseFtpConnection;
038: import net.sf.drftpd.master.SlaveFileException;
039: import net.sf.drftpd.master.command.CommandManagerFactory;
040:
041: import org.apache.log4j.Level;
042: import org.apache.log4j.Logger;
043: import org.drftpd.Bytes;
044: import org.drftpd.GlobalContext;
045: import org.drftpd.PropertyHelper;
046: import org.drftpd.commands.Reply;
047: import org.drftpd.commands.UserManagement;
048: import org.drftpd.plugins.RaceStatistics;
049: import org.drftpd.plugins.Statistics;
050: import org.drftpd.slave.Slave;
051: import org.drftpd.usermanager.NoSuchUserException;
052: import org.drftpd.usermanager.User;
053: import org.drftpd.usermanager.UserFileException;
054:
055: /**
056: * @version $Id: ConnectionManager.java 1513 2006-10-13 22:41:08Z tdsoul $
057: */
058: public class ConnectionManager {
059: private static final Logger logger = Logger
060: .getLogger(ConnectionManager.class.getName());
061: private CommandManagerFactory _commandManagerFactory;
062: private List<BaseFtpConnection> _conns = Collections
063: .synchronizedList(new ArrayList<BaseFtpConnection>());
064: protected GlobalContext _gctx;
065:
066: protected ConnectionManager() {
067: }
068:
069: public ConnectionManager(Properties cfg, Properties slaveCfg,
070: String cfgFileName) throws SlaveFileException {
071: _gctx = new GlobalContext(cfg, cfgFileName, this );
072:
073: if (slaveCfg != null) {
074: try {
075: new Slave(slaveCfg);
076: } catch (IOException ex) { // RemoteException extends IOException
077: throw new FatalException(ex);
078: }
079: }
080:
081: _commandManagerFactory = new CommandManagerFactory(this );
082:
083: getGlobalContext().addFtpListener(new RaceStatistics());
084: getGlobalContext().addFtpListener(new Statistics());
085:
086: loadTimer();
087: getGlobalContext().getSlaveManager().addShutdownHook();
088: }
089:
090: public static void main(String[] args) {
091: System.out.println(Slave.VERSION + " master server starting.");
092: System.out.println("http://drftpd.org/");
093: System.out
094: .println("Further logging will be done using (mostly) log4j, check logs/");
095:
096: try {
097: String cfgFileName;
098:
099: if (args.length >= 1) {
100: cfgFileName = args[0];
101: } else {
102: cfgFileName = "drftpd.conf";
103: }
104:
105: String slaveCfgFileName;
106:
107: if (args.length >= 2) {
108: slaveCfgFileName = args[1];
109: } else {
110: slaveCfgFileName = "slave.conf";
111: }
112:
113: /** load master config **/
114: Properties cfg = new Properties();
115: cfg.load(new FileInputStream(cfgFileName));
116:
117: /** load slave config **/
118: Properties slaveCfg; //used as a flag for if localslave=true
119:
120: if (cfg.getProperty("master.localslave", "false")
121: .equalsIgnoreCase("true")) {
122: slaveCfg = new Properties();
123: slaveCfg.load(new FileInputStream(slaveCfgFileName));
124: } else {
125: slaveCfg = null;
126: }
127:
128: logger.info("Starting ConnectionManager");
129:
130: ConnectionManager mgr = new ConnectionManager(cfg,
131: slaveCfg, cfgFileName);
132:
133: /** listen for connections **/
134: String bindip = null;
135: ServerSocket server = null;
136: boolean useIP;
137:
138: try {
139: bindip = PropertyHelper.getProperty(cfg, "master.ip");
140: if (bindip.equals(""))
141: useIP = false;
142: else
143: useIP = true;
144: } catch (NullPointerException e) {
145: useIP = false;
146: }
147:
148: if (useIP) {
149: server = new ServerSocket();
150: server.bind(new InetSocketAddress(bindip, Integer
151: .parseInt(PropertyHelper.getProperty(cfg,
152: "master.port"))));
153: logger.info("Listening on " + server.getInetAddress()
154: + ":" + server.getLocalPort());
155: } else {
156: server = new ServerSocket(Integer
157: .parseInt(PropertyHelper.getProperty(cfg,
158: "master.port")));
159: logger.info("Listening on port "
160: + server.getLocalPort());
161: }
162:
163: while (true) {
164: mgr.start(server.accept());
165: }
166:
167: //catches subclasses of Error and Exception
168: } catch (Throwable th) {
169: logger.error("", th);
170: System.exit(0);
171:
172: return;
173: }
174: }
175:
176: public Reply canLogin(BaseFtpConnection baseconn, User user) {
177: int count = getGlobalContext().getConfig().getMaxUsersTotal();
178:
179: //Math.max if the integer wraps
180: if (user.isExempt()) {
181: count = Math.max(count, count
182: + getGlobalContext().getConfig()
183: .getMaxUsersExempt());
184: }
185:
186: // not >= because baseconn is already included
187: if (_conns.size() > count) {
188: return new Reply(550, "The site is full, try again later.");
189: }
190:
191: int userCount = 0;
192: int ipCount = 0;
193:
194: synchronized (_conns) {
195: for (Iterator iter = _conns.iterator(); iter.hasNext();) {
196: BaseFtpConnection tempConnection = (BaseFtpConnection) iter
197: .next();
198:
199: try {
200: User tempUser = tempConnection.getUser();
201:
202: if (tempUser.getName().equals(user.getName())) {
203: userCount++;
204:
205: if (tempConnection.getClientAddress().equals(
206: baseconn.getClientAddress())) {
207: ipCount++;
208: }
209: }
210: } catch (NoSuchUserException ex) {
211: // do nothing, we found our current connection, baseconn = tempConnection
212: }
213: }
214: }
215:
216: if (user.getKeyedMap().getObjectInt(UserManagement.MAXLOGINS) > 0) {
217: if (user.getKeyedMap().getObjectInt(
218: UserManagement.MAXLOGINS) <= userCount) {
219: return new Reply(530,
220: "Sorry, your account is restricted to "
221: + user.getKeyedMap().getObjectInt(
222: UserManagement.MAXLOGINS)
223: + " simultaneous logins.");
224: }
225: }
226: if (user.getKeyedMap().getObjectInt(UserManagement.MAXLOGINSIP) > 0) {
227: if (user.getKeyedMap().getObjectInt(
228: UserManagement.MAXLOGINSIP) <= ipCount) {
229: return new Reply(530,
230: "Sorry, your maximum number of connections from this IP ("
231: + user.getKeyedMap().getObjectInt(
232: UserManagement.MAXLOGINSIP)
233: + ") has been reached.");
234: }
235: }
236:
237: if (user.getKeyedMap().getObjectDate(UserManagement.BAN_TIME)
238: .getTime() > System.currentTimeMillis()) {
239: return new Reply(530, "Sorry you are banned until "
240: + user.getKeyedMap().getObjectDate(
241: UserManagement.BAN_TIME)
242: + "! ("
243: + user.getKeyedMap().getObjectString(
244: UserManagement.BAN_REASON) + ")");
245: }
246:
247: if (!baseconn.isSecure()
248: && getGlobalContext().getConfig().checkPermission(
249: "userrejectinsecure", user)) {
250: return new Reply(530, "USE SECURE CONNECTION");
251: } else if (baseconn.isSecure()
252: && getGlobalContext().getConfig().checkPermission(
253: "userrejectsecure", user)) {
254: return new Reply(530, "USE INSECURE CONNECTION");
255: }
256:
257: return null; // everything passed
258: }
259:
260: public void dispatchFtpEvent(Event event) {
261: getGlobalContext().dispatchFtpEvent(event);
262: }
263:
264: public CommandManagerFactory getCommandManagerFactory() {
265: return _commandManagerFactory;
266: }
267:
268: /**
269: * returns a <code>Collection</code> of current connections
270: */
271: public List<BaseFtpConnection> getConnections() {
272: return _conns;
273: }
274:
275: public GlobalContext getGlobalContext() {
276: if (_gctx == null) {
277: throw new NullPointerException();
278: }
279: return _gctx;
280: }
281:
282: private void loadTimer() {
283: /* TimerTask timerLogoutIdle = new TimerTask() {
284: public void run() {
285: try {
286: timerLogoutIdle();
287: } catch (Throwable t) {
288: logger.error("Error in timerLogoutIdle TimerTask", t);
289: }
290: }
291: };
292:
293: //run every 10 seconds
294: getGlobalContext().getTimer().schedule(timerLogoutIdle, 10 * 1000, 10 * 1000);*/
295:
296: TimerTask timerSave = new TimerTask() {
297: public void run() {
298: try {
299: getGlobalContext().getSlaveManager().saveFilelist();
300:
301: try {
302: getGlobalContext().getUserManager().saveAll();
303: } catch (UserFileException e) {
304: logger.log(Level.FATAL,
305: "Error saving all users", e);
306: }
307: } catch (Throwable t) {
308: logger.error("Error in timerSave TimerTask", t);
309: }
310: }
311: };
312: /* TimerTask timerGarbageCollect = new TimerTask() {
313: public void run() {
314: logger.debug("Memory free before GC :"
315: + Bytes.formatBytes(Runtime.getRuntime().freeMemory())
316: + "/"
317: + Bytes.formatBytes(Runtime.getRuntime().totalMemory()));
318: System.gc();
319: logger.debug("Memory free after GC :"
320: + Bytes.formatBytes(Runtime.getRuntime().freeMemory())
321: + "/"
322: + Bytes.formatBytes(Runtime.getRuntime().totalMemory()));
323: }
324: };*/
325:
326: // run every hour
327: getGlobalContext().getTimer().schedule(timerSave,
328: 60 * 60 * 1000, 60 * 60 * 1000);
329: // run every minute
330: // getGlobalContext().getTimer().schedule(timerGarbageCollect, 60 * 1000, 60 * 1000);
331: }
332:
333: public void remove(BaseFtpConnection conn) {
334: if (!_conns.remove(conn)) {
335: throw new RuntimeException(
336: "connections.remove() returned false.");
337: }
338:
339: if (getGlobalContext().isShutdown() && _conns.isEmpty()) {
340: // _slaveManager.saveFilelist();
341: // try {
342: // getUserManager().saveAll();
343: // } catch (UserFileException e) {
344: // logger.log(Level.WARN, "Failed to save all userfiles", e);
345: // }
346: logger.info("Shutdown complete, exiting");
347: System.exit(0);
348: }
349: }
350:
351: public void shutdownPrivate(String message) {
352: for (BaseFtpConnection conn : new ArrayList<BaseFtpConnection>(
353: getConnections())) {
354: conn.stop(message);
355: }
356: }
357:
358: public void start(Socket sock) throws IOException {
359: if (getGlobalContext().isShutdown()) {
360: new PrintWriter(sock.getOutputStream()).println("421 "
361: + getGlobalContext().getShutdownMessage());
362: sock.close();
363: return;
364: }
365:
366: /*
367: * Reserved for Implicit SSL, TODO
368: * if(sock instanceof SSLSocket)
369: {
370: SSLSocket sslsock = (SSLSocket) sock;
371: sslsock.setUseClientMode(false);
372: sslsock.startHandshake();
373: sock = sslsock;
374: }*/
375:
376: BaseFtpConnection conn = new BaseFtpConnection(
377: getGlobalContext(), sock);
378:
379: _conns.add(conn);
380: conn.start();
381: }
382:
383: /* public void timerLogoutIdle() {
384: long currTime = System.currentTimeMillis();
385: ArrayList<BaseFtpConnection> conns = new ArrayList<BaseFtpConnection>(_conns);
386:
387: for (Iterator i = conns.iterator(); i.hasNext();) {
388: BaseFtpConnection conn = (BaseFtpConnection) i.next();
389:
390: int idle = (int) ((currTime - conn.getLastActive()) / 1000);
391: int maxIdleTime;
392:
393: try {
394: maxIdleTime = conn.getUser().getIdleTime();
395:
396: if (maxIdleTime == 0) {
397: maxIdleTime = idleTimeout;
398: }
399: } catch (NoSuchUserException e) {
400: maxIdleTime = idleTimeout;
401: }
402:
403: if (!conn.isExecuting() && (idle >= maxIdleTime)) {
404: // idle time expired, logout user.
405: conn.stop("Idle time expired: " + maxIdleTime + "s");
406: }
407: }
408: }*/
409:
410: public void setGlobalContext(GlobalContext gctx) {
411: _gctx = gctx;
412: }
413: }
|