Så er mit værk færdigt!
Dette er en IRC bot, der placerer sig på vores IRC channel og holder fast i forbindelsen indtil den skal noget. Samtidig åbnes en socket, som er klar til at modtage data der skal skrives i chatten. Skide genialt, da dette kan bruges i samarbejde med en masse andre programmer, her bruger vi den så sammen med MyBB, så derfor har jeg kaldt den BB2IRC.
Det den gør er simpelt, når en besked bliver oprettet på forummet, sendes en besked til bottens åbne socket (INC_IP:INC_PORT).
Med PHP har jeg gjort dette med noget så simpelt som dette:
<?php
$msgtype = array("oprettet en tråd med titlen", "besvaret tråden");
$title = $_GET['title'];
$user = $_GET['user'];
$link = "http://shellsec.pw/showthread.php?tid=" . $_GET['tid'] . "&pid=" . $_GET['pid'] . "#pid" . $_GET['pid'];
$msg = $user . " har " . $msgtype[$_GET['type']] . " \"" . $title . "\"" . $INC_DELIM . $link;
$sockfd = fsockopen($INC_IP, $INC_PORT, $errno, $errstr, 120);
if(!$sockfd){
die();
}else{
fwrite($sockfd, $msg);
fclose($sockfd);
}
?>
Et eksempel på en besked oprettes således ved en query til serveren med denne querystring:
?user=Doctor%20Blue&type=0&title=[C++]%20IRC%20bot%20til%20overvågning%20af%20forum%20posts&tid=207&pid=
$INC_DELIM repræsenterer det tegn, der også sættes som konstanten INC_DELIM i botten, og er altså det tegn der indikerer en ny linje.
Output vil altså være:
Citer:Botnick: Doctor Blue har oprettet en tråd med titlen [C++] IRC bot til overvågning af forum posts
Botnick: http://shellsec.pw/showthread.php?tid=207&pid=#pid
og så har vi selve botten her:
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sstream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <fcntl.h>
using namespace std;
static const string IRC_IP = "208.64.123.83"; // IP of the IRC server, this one is irc.voxanon.net
static const int IRC_PORT = 6667; // Port of the IRC server (SSL/TLS IS NOT SUPPORTED)
static const string NSNAME = "NickServ"; // Name of the nickname service (Normally nickserv, but sometimes nickserv @ service.network.tld, CASE SENSITIVE)
static const string NICKNAME = "Shellsec"; // Nickname of the bot
static const string NICKPASS = ""; // Password for nickserv. Leave empty if nick isn't registered
static const string CHANNEL = "Shellsec"; // Name of IRC channel (No hashtag)
static const string INC_IP = "127.0.0.1"; // IP address for incoming connections (Messages to forward)
static const int INC_PORT = 6111; // Port for incoming connections
static const string INC_DELIM = ";"; // Newline character in incoming stream.
bool SetSocketBlockingEnabled(int fd, bool blocking)
{
if (fd < 0) return false;
#ifdef WIN32
unsigned long mode = blocking ? 0 : 1;
return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? true : false;
#else
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) return false;
flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
return (fcntl(fd, F_SETFL, flags) == 0) ? true : false;
#endif
}
class IRC{
private:
// Socket File Descriptor
int sockfd, n, listfd, clientfd;
// Stream buffers
char recvBuff[1024];
char listBuff[1024];
string sendBuff;
// Structure for socket connection address
struct sockaddr_in irc_addr, list_addr;
public:
void send(string message){
sendBuff = message + "\r\n";
write(sockfd, sendBuff.c_str(), strlen(sendBuff.c_str()));
}
void open(string ip, int port){
cout << "Establishing connection\n";
// Allocate memory for the receiving buffers
memset(recvBuff, '0', sizeof(recvBuff));
memset(listBuff, '0', sizeof(listBuff));
// Create IPv4 sockets and check for errors
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
cout << "Error: Could not create socket\n";
exit(1);
}
cout << "Creating socket for listening";
if((listfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
cout << "Error: Could not create listening socket\n";
exit(1);
}
// Specify connection parameters
// Convert IP address to network format
if(inet_pton(AF_INET, ip.c_str(), &irc_addr.sin_addr) <= 0){
cout << "Error: Failed to read IP address\n";
exit(1);
}
if(inet_pton(AF_INET, INC_IP.c_str(), &list_addr.sin_addr) <= 0){
cout << "Error: Failed to read incoming IP address\n";
exit(1);
}
// Specify Internet Protocol and port number
irc_addr.sin_family = AF_INET;
irc_addr.sin_port = htons(IRC_PORT);
list_addr.sin_family = AF_INET;
list_addr.sin_port = htons(INC_PORT);
// Attempt to establish a connection using the socket
if(connect(sockfd, (struct sockaddr *)&irc_addr,sizeof(irc_addr)) < 0){
cout << "Error: Could not connect to server\n";
exit(1);
}
// Attempt to bind the listening socket
cout << "Binding listener";
if(bind(listfd, (struct sockaddr *)&list_addr, sizeof(list_addr)) < 0){
cout << "Error: Failed to bind to listening IP";
exit(1);
}
// Start the listener
cout << "Starting listener";
if(listen(listfd, 3) < 0){
cout << "Error: Failed to start listener";
exit(1);
}
}
void poolListener(){
struct sockaddr_in client_addr;
socklen_t clilen = sizeof client_addr;
stringstream stream;
string word;
SetSocketBlockingEnabled(listfd, false);
clientfd = accept(listfd, (struct sockaddr *)&client_addr, &clilen);
if(clientfd > 0){
cout << "Client connected, recieving message: ";
if((n = read(clientfd, listBuff, sizeof(listBuff) - 1)) > 0)
listBuff[n] = 0;
stringstream stream(listBuff);
while( getline(stream, word, ';') ){
cout << word << ";";
send("PRIVMSG #ShellSec :" + word);
}
cout << "\n";
}
}
string monitor(){
istringstream iss;
while(true){
poolListener();
if((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0)
recvBuff[n] = 0;
istringstream iss(recvBuff);
string streampart;
do{
iss >> streampart;
if(streampart == "Auth"){
send("NICK " + NICKNAME);
send("USER " + NICKNAME + " * * :" + NICKNAME);
}
if(streampart == "001"){
send("JOIN #" + CHANNEL);
if(NICKPASS != ""){
send("PRIVMSG " + NSNAME + " : IDENTIFY " + NICKPASS);
}
}
if(streampart == "PING"){
send("PONG");
cout << "\n>PONG\n";
}
if(streampart == "PRIVMSG"){
iss >> streampart;
if(streampart == "#ShellSec"){
iss >> streampart;
if(streampart == ":" + NICKNAME){
send("PRIVMSG #" + CHANNEL + " :Reporting for duty!");
}
}
}
}while(iss);
if(fputs(recvBuff, stdout) == EOF){
cout << "Error: Couldn't output to stdout";
}
if(n < 0){
cout << "Error: Read failed";
}
}
}
} conn;
int main() {
// Startup message
cout << "BB2IRC is starting...\n";
cout << "Made by Doctor Blue\n";
sleep(3);
conn.open(IRC_IP, IRC_PORT); // Open connection to IRC server
conn.monitor();
exit(EXIT_SUCCESS);
return 0;
}
Hele molevitten er cross-compatible, altså det funger på Linux og Windows, og i princippet burde det også virke på Mac, men jeg har ikke testet :)
Hyggehejsa, nyd mit arbejde når Morph får det op at køre :)