Author: Jacob Mitchell
Date: 10/24/25
Description: Distributed File Transfer Protocol (FTP) utilizing the standard socket interface. Based on the classic FTP (RFC 959) to build a federated file network with dynamic data management.
P2/
├─ ServerA/
│ ├─ server # compiled binary
│ ├─ server.conf # config (setup for instructor grading)
│ ├─ server.cpp
│ ├─ ftp_session.{h,cpp}
│ ├─ server_utils.{h,cpp}
│ ├─ ftp_codes.{h,cpp}
│ ├─ peer_client.{h,cpp}
│ ├─ peer_manager.{h,cpp}
│ ├─ cidr_utils.{h,cpp}
│ ├─ db/ # jail root for this server’s files
│ └─ Makefile # make
│
├─ (ServerX, etc...)
Note
db/ is the jail root of each server.
ALL paths are evaluated relative to it.
- Run
makewithin correct folder structure
If make file fails, follow steps below:
- Run
g++in terminal with the following command:
g++ -std=c++20 -pthread server.cpp ftp_session.cpp server_utils.cpp ftp_codes.cpp peer_client.cpp peer_manager.cpp cidr_utils.cpp -o server
- Place the compiled
server.exeinto a separate ServerA/B/C/Etc folder each with its own db folder filled with its respective data. - Execute the
./serverfollowed by aserver.confargument.
./server server.conf
- Connect to the server using telnet or nc with the appropriate ip and port number. For example,
telnet 127.0.0.1 2121
- Sign into server with authorization by using:
USER anonymous
- Navigate to desired files before executing
PASVwithCWD - Enter
PASVmode by executingPASV - Open a new terminal instance and use
telnetorncagain and connect to the displayed ephemeral port forming the data pipe. This port is calculated by(p1 * 256 + p2)For example,
telnet 127.0.0.1 40594
Where 40594 represents the calculated port number.
- Run
LISTto retrieve the linux based information about the current working directory and its content - The data pipe connection will then transmit its data and automatically close. You will need to activate
PASVonce more and reconnect to a new ephemeral port before retrieving more data.
USER anonymous
CWD ..
250 Requested file action okay, completed.
PASV
227 Entering Passive Mode (127,0,0,1,p1,p2) # where ((p1* 256) + p2) is our ephemeral port
LIST
150 File status okay; about to open data connection.
226 Closing data connection. Requested file action successful.
Where the data transmitted is received within the second connected terminal after connecting to the provided ip and ephemeral port.
Example data connection output for above exectued commands:
drwxr-xr-x 1 local 0 Oct 22 16:30 comp.security
drwxr-xr-x 1 local 0 Oct 22 16:30 news.world
Where a federated listing may look like:
drwxr-xr-x 1 local 0 Oct 22 16:30 comp.security
drwxr-xr-x 1 local 0 Oct 22 16:30 news.world
drwxr-xr-x 1 peer 0 Oct 22 16:35 reddit
- RFC 959 style control channel all commands/replies are ASCII lines terminated with \r\n.
- Data path is raw bytes: no CRLF translation on data sockets so files are streamed exactly as stored.
- Passive mode
PASVrequired beforeLIST/RETRexcept forRETRexecution on remote file from peer mediated root. - One ephemeral data listener at a time. Any CWD invalidates a pending PASV.
- Federation behavior:
- Servers auto discover peers within the configured CIDR range supplied within
server.conf - Each server listens on a base_port, pulled from
server.conffor clients and base_port+200 for peer control sessions. - At
/(root),LISTreturns a merged view: local entries + discovered peer entries. - When a requested file/dir is remote, the serving server transparently relays the peer’s
227so the client’s data connection goes directly to the peer.
- Servers auto discover peers within the configured CIDR range supplied within
- Peer sessions never re-federate: once a server is acting as a peer (receiving
USER peer@A), it serves only its local jail. Preventing recursive server calls. - Jail enforcement: all resolved paths must stay under
db/. Absolute paths are treated relative to jail (/meansdb/). - Ownership display: listings show only an owner token (
localorpeer). - Directory sizes shown as 0 in listings to match our project’s sample output.
- One client data transfer socket at a time: data socket opens on
PASV, is used by the nextLIST/RETR, and is then closed. New transfer requires newPASV.
Case Insensitive
- USER
<name>: login (acceptsanonymousfor users andpeer@<ip>for server auth - PWD / CWD / CDUP: navigate within our jail; returning to
/returns to local/ALWAYS - PASV: opens ephemeral data listener (local) or relays peer’s
227(remote) - LIST: local list of content when in local namespace. When in a peer directory, relayed to that peer. Or peer mediated when within root / shared filespaces.
- RETR
<path>: streams file bytes. If remote and PASV not yet armed for that peer, server replies with the peer’s227first. - QUIT: close control connection
server.confparameters are fixed for project submission (do not rename keys):PORT— client control listening portPEER_SUBNET— CIDR range for auto discovery (e.g.192.168.0.0/27for 32 addresses scanned/28for 16, etc)PEER_INTERVAL— scan interval ( in seconds)
- Listener ports:
- Client listener:
PORT - Peer listener:
PORT + 200
- Client listener:
- Each server MUST have a
db/directory at runtime. (Can assume)
- Ctrl-C slow/unresponsive: Due to the connection sockets and frequent scans / sleeps - sometimes we see a delay in shutting down the server.
- Cannot connect to PASV port: verify you’re connecting to the IP/port from the most recent
227. AnyCWDinvalidates previousPASV - LIST in remote dir returns 425: call
PASVafter entering the remote dir so the server can relay the peer’s227. - RETR from
/for a remote file returns new PASV: This is the expected behavior. Due to the prematurePASVcall the server will respond with peer’s227. RunPASV, connect to that port, then retryRETR.
- Running
RETRwithin a shared filespace, such as '/', on a remote file before runningPASVmakes the server automatically enterPASVmode. This is an artifact of my implemented fix of prematurePASVon peer mediated space like/->RETR <remote file>-> should prompt the server to respond with a newPASVport on the corresponding server. - User executes
CWDinto remote server -> server disconnects -> user is left on zombie dir. However, no errors were observed when trying to execute future commands on this zombie dir. Eventually user can return to root dir and continue to perform actions normally.
- Test duplicate
file/dirnames within separate servers. - Implement tie breaker functionality / alert when user executes commands on files with same names.
- Implement full RFC functionality