This project implements an IRC (Internet Relay Chat) server using C++98 and Linux system calls. The server handles multiple client connections, channels, and various IRC commands.
A socket is an endpoint for communication between two machines over a network. It's a software construct that provides a bidirectional communication channel between processes, either on the same machine or across different machines. In this project, we use TCP sockets which provide:
- Reliable, ordered, and error-checked delivery of data
- Connection-oriented communication
- Stream-based data transfer
epoll is a Linux-specific I/O event notification mechanism that efficiently monitors multiple file descriptors for events. It's an improvement over older mechanisms like select() and poll() because:
- It scales better with large numbers of file descriptors
- It uses a more efficient event notification system
- It doesn't require scanning all file descriptors on each call
- It supports edge-triggered and level-triggered modes
The epoll API consists of three main system calls:
epoll_create(): Creates an epoll instanceepoll_ctl(): Registers/unregisters file descriptorsepoll_wait(): Waits for events on registered file descriptors
-
socket()
- Creates a new socket
- Usage:
socket(AF_INET, SOCK_STREAM, 0) - Returns a file descriptor for the new socket
- AF_INET: IPv4 protocol family
- SOCK_STREAM: TCP socket type
-
setsockopt()
- Sets socket options
- Usage:
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) - SO_REUSEADDR: Allows reuse of local addresses
- Important for server restarts to avoid "Address already in use" errors
-
bind()
- Binds a socket to an address and port
- Usage:
bind(socket_fd, (struct sockaddr *)&address, sizeof(address)) - Associates the socket with a specific port and IP address
- Returns 0 on success, -1 on error
-
listen()
- Marks the socket as passive (server socket)
- Usage:
listen(socket_fd, backlog) - backlog: Maximum length of the queue of pending connections
- Returns 0 on success, -1 on error
-
epoll_create()
- Creates an epoll instance
- Usage:
epoll_create1(0) - Returns a file descriptor for the epoll instance
- Used for efficient I/O event monitoring
-
fcntl()
- Manipulates file descriptor properties
- Usage:
fcntl(fd, F_SETFL, O_NONBLOCK) - Sets socket to non-blocking mode
- Important for asynchronous I/O operations
-
accept()
- Accepts a new connection
- Usage:
accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen) - Returns a new socket descriptor for the accepted connection
- Creates a new socket for client communication
-
epoll_ctl()
- Controls an epoll instance
- Usage:
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) - Adds/modifies/removes file descriptors to/from the epoll instance
- EPOLL_CTL_ADD: Adds a new file descriptor
-
epoll_wait()
- Waits for I/O events
- Usage:
epoll_wait(epoll_fd, events, MAX_EVENTS, timeout) - Returns the number of ready file descriptors
- Used for event-driven programming
- Clone the repository
- Run
maketo build the project - Start the server:
./ircserv <port> <password>
Example:
./ircserv 6667 password123Note
For the project, the correction sheets specifies that you can "do some test with the IRC client and nc at the same time". Each time, both syntax are written. But they have to be executed in two diferent windows.
- Connect to the server:
nc localhost <port>- Register with the server:
PASS <password>
NICK <nickname>
USER <username> <hostname> <servername> :<realname>
Example:
nc localhost 6667PASS password123NICK user1USER user1 0 * :John Doe- Install sic:
sudo apt-get install sic- Connect to the server:
sic -h localhost -p <port> -n <nickname> -k <password>Example:
sic -h localhost -p 6667 -n user1 -k password123Note
By setting sic that way, you will not have to use PASS ans NICK commands. The next one to enter will be USER.
- Purpose: Set your password
- sic Syntax:
:PASS <password> - netcat Syntax:
PASS <password> - Example:
# sic :PASS john_doe # netcat PASS john_doe
-
Purpose: Set your nickname
-
sic Syntax:
:NICK <new_nickname> -
netcat Syntax:
NICK <new_nickname> -
Example:
# sic :NICK john_doe # netcat NICK john_doe
-
Purpose: Set your Username
-
sic Syntax:
:USER <username> <hostname> <servername> :<realname> -
netcat Syntax:
USER <username> <hostname> <servername> :<realname> -
Example:
# sic :USER user1 0 * :John Doe # netcat USER user1 0 * :John Doe
Note
To join a channel, you can use the '#' or the '&' before the channel's name. But #channel is different from &channel.
- Purpose: Join a channel
- sic Syntax:
:JOIN &<channel_name> - netcat Syntax:
JOIN &<channel_name> - Example:
# sic :JOIN &general :JOIN #general # netcat JOIN &general JOIN #general
- Purpose: Send a private message to a user or channel
- sic Syntax:
:PRIVMSG <target> <message> - netcat Syntax:
PRIVMSG <target> <message> - Example:
# sic :PRIVMSG &general Hello everyone! :PRIVMSG john_doe Hi there! # netcat PRIVMSG &general Hello everyone! PRIVMSG john_doe Hi there!
- Purpose: Set or view channel topic
- sic Syntax:
:TOPIC &<channel_name> :<new_topic> - netcat Syntax:
TOPIC &<channel_name> :<new_topic> - Example:
# sic :TOPIC &general :Welcome to the general chat! # netcat TOPIC &general :Welcome to the general chat!
- Purpose: Remove a user from a channel
- sic Syntax:
:KICK &<channel_name> <nickname> :<reason> - netcat Syntax:
KICK &<channel_name> <nickname> :<reason> - Example:
# sic :KICK &general john_doe :KICK &general john_doe :Being disruptive # netcat KICK &general john_doe KICK &general john_doe :Being disruptive
Note
The reason argument is optionnal.
- Purpose: Invite a user to join a channel
- sic Syntax:
:INVITE <nickname> &<channel_name> - netcat Syntax:
INVITE <nickname> &<channel_name> - Example:
# sic :INVITE john_doe &general # netcat INVITE john_doe &general
-
Purpose: Set channel modes to control channel behavior and user permissions
-
Available Modes:
+i/-i: Invite-only mode+i: Only users with an invite can join-i: Anyone can join (default)
+t/-t: Topic protection+t: Only operators can change the topic-t: Anyone can change the topic (default)
+k/-k: Channel key (password)+k <key>: Set a password to join the channel-k: Remove the password
+o/-o: Operator status+o <nickname>: Give operator status to a user-o <nickname>: Remove operator status from a user
+l/-l: User limit+l <number>: Set maximum number of users-l: Remove user limit
-
Examples of Multiple Modes:
# sic :MODE &general +i # Set invite-only :MODE &general +t # Set topic protection :MODE &general +k password123 # Set channel password :MODE &general +o john_doe # Make john_doe an operator :MODE &general +l 10 # Set user limit to 10 # Combine multiple modes in one command :MODE &general +itk password123 # Set invite-only, topic protection, and password :MODE &general +ol john_doe 10 # Make john_doe operator and set user limit # netcat MODE &general +i # Set invite-only MODE &general +t # Set topic protection MODE &general +k password123 # Set channel password MODE &general +o john_doe # Make john_doe an operator MODE &general +l 10 # Set user limit to 10 # Combine multiple modes in one command MODE &general +itk password123 # Set invite-only, topic protection, and password MODE &general +o john_doe +l 10 # Make john_doe operator and set user limit
Note
When combining multiple modes in one command:
- Modes that require parameters (like +k and +l) must be specified in the correct order
- The parameters must match the order of the modes that require them
- Example:
MODE &channel +kl password 10is correct, butMODE &channel +lk 10 passwordis not
- Purpose: Transfer files between users
- sic Syntax:
:DCC <command> <arguments> - netcat Syntax:
DCC <command> <arguments> - Examples:
# sic :DCC SEND john_doe file.txt :DCC ACCEPT file.txt # netcat DCC SEND john_doe file.txt DCC ACCEPT file.txt
- File downloaded: If the file is accepted, you will receive the same file withthe extension "_download" at the end of the filename. You may have to rename it to suits your needs.
Note
The file size doesn't have to exceed 1MB.
- Purpose: Play casino games
Note
For best results, it's better to play on netcat.
- Join the casino:
# netcat
JOIN #casino
# sic
:JOIN #casino- How to play ? - Let's ask the Croupier (Dealer).
# netcat
PRIVMSG Croupier How to play ?
# sic
:PRIVMSG Croupier How to play ? - You can start betting.
- netcat Syntax:
BET <head or tail> <amount of money> - sic Syntax:
:BET <head or tail> <amount of money> - Examples:
# netcat BET head 5 # sic :BET head 5
-
Constructor:
Server(int port, std::string password)- Initializes the server socket
- Sets up epoll for event handling
- Configures socket options
-
Key Methods:
run(): Main server loophandleClientMessage(int client_fd): Processes client messagesremoveClient(int fd): Removes a client from the serverclientLog(int fd, std::string message): Logs client activities
-
Constructor:
Client(int fd)- Initializes a new client connection
- Sets up client-specific properties
-
Key Methods:
getFd(): Returns client's file descriptorgetNickname(): Returns client's nicknamesetNickname(std::string nickname): Sets client's nicknamesendMessage(std::string message): Sends a message to the client
-
Constructor:
Channel(std::string name)- Creates a new channel
- Initializes channel properties
-
Key Methods:
addClient(Client* client): Adds a client to the channelremoveClient(Client* client): Removes a client from the channelbroadcastMessage(std::string message): Sends a message to all channel membersgetClients(): Returns list of channel clients
- C++ compiler with C++98 support
- Linux system (for epoll)
- Make
- sic (for official client testing)
- netcat (for basic testing)
- Password protection for server access
- Input validation for all commands
- Proper client disconnection handling
- Memory leak prevention in destructors
